Commit ddccdfbc authored by mergify[bot]'s avatar mergify[bot] Committed by GitHub

Merge branch 'develop' into inphi/proxyd-log

parents b9bb1a98 d11d4ad7
---
'@eth-optimism/l2geth': patch
---
Has l2geth return a NonceToHigh response if the txn nonce is greater than the expected nonce.
......@@ -555,8 +555,10 @@ func (pool *TxPool) validateTx(tx *types.Transaction, local bool) error {
}
// Ensure the transaction adheres to nonce ordering
if rcfg.UsingOVM {
if pool.currentState.GetNonce(from) != tx.Nonce() {
if pool.currentState.GetNonce(from) > tx.Nonce() {
return ErrNonceTooLow
} else if pool.currentState.GetNonce(from) < tx.Nonce() {
return ErrNonceTooHigh
}
} else {
if pool.currentState.GetNonce(from) > tx.Nonce() {
......
package main
import (
"errors"
"os"
"github.com/mattn/go-isatty"
"github.com/urfave/cli/v2"
"github.com/ethereum-optimism/optimism/op-chain-ops/eof"
"github.com/ethereum/go-ethereum/log"
)
func main() {
log.Root().SetHandler(log.StreamHandler(os.Stderr, log.TerminalFormat(isatty.IsTerminal(os.Stderr.Fd()))))
app := &cli.App{
Name: "eof-crawler",
Usage: "Scan a Geth database for EOF-prefixed contracts",
Flags: []cli.Flag{
&cli.StringFlag{
Name: "db-path",
Usage: "Path to the geth LevelDB",
},
&cli.StringFlag{
Name: "out",
Value: "eof-contracts.json",
Usage: "Path to the output file",
},
},
Action: func(ctx *cli.Context) error {
dbPath := ctx.String("db-path")
if len(dbPath) == 0 {
return errors.New("Must specify a db-path")
}
out := ctx.String("out")
return eof.IndexEOFContracts(dbPath, out)
},
}
if err := app.Run(os.Args); err != nil {
log.Crit("error indexing state", "err", err)
}
}
# `eof-crawler`
Simple CLI tool to scan all accounts in a geth LevelDB for contracts that begin with the EOF prefix.
## Usage
1. Pass the directory of the Geth DB into the tool
```sh
go run ./cmd/eof-crawler/main.go --db-path <db_path> [--out <out_file>]
```
2. Once the indexing has completed, an array of all EOF-prefixed contracts will be written to `eof_contracts.json` or the designated output file.
package eof
import (
"bytes"
"encoding/json"
"errors"
"fmt"
"log"
"os"
"time"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/common/hexutil"
"github.com/ethereum/go-ethereum/core/rawdb"
"github.com/ethereum/go-ethereum/core/state"
"github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/crypto"
"github.com/ethereum/go-ethereum/rlp"
"github.com/ethereum/go-ethereum/trie"
)
// Account represents an account in the state.
type Account struct {
Balance string `json:"balance"`
Nonce uint64 `json:"nonce"`
Root hexutil.Bytes `json:"root"`
CodeHash hexutil.Bytes `json:"codeHash"`
Code hexutil.Bytes `json:"code,omitempty"`
Address common.Address `json:"address,omitempty"`
SecureKey hexutil.Bytes `json:"key,omitempty"`
}
// emptyCodeHash is the known hash of an account with no code.
var emptyCodeHash = crypto.Keccak256(nil)
// IndexEOFContracts indexes all the EOF contracts in the state trie of the head block
// for the given db and writes them to a JSON file.
func IndexEOFContracts(dbPath string, out string) error {
// Open an existing Ethereum database
db, err := rawdb.NewLevelDBDatabase(dbPath, 16, 16, "", true)
if err != nil {
return fmt.Errorf("Failed to open database: %w", err)
}
stateDB := state.NewDatabase(db)
// Retrieve the head block
hash := rawdb.ReadHeadBlockHash(db)
number := rawdb.ReadHeaderNumber(db, hash)
if number == nil {
return errors.New("Failed to retrieve head block number")
}
head := rawdb.ReadBlock(db, hash, *number)
if head == nil {
return errors.New("Failed to retrieve head block")
}
// Retrieve the state belonging to the head block
st, err := trie.New(trie.StateTrieID(head.Root()), trie.NewDatabase(db))
if err != nil {
return fmt.Errorf("Failed to retrieve state trie: %w", err)
}
log.Printf("Indexing state trie at head block #%d [0x%x]", *number, hash)
// Iterate over the entire account trie to search for EOF-prefixed contracts
start := time.Now()
missingPreimages := uint64(0)
eoas := uint64(0)
nonEofContracts := uint64(0)
eofContracts := make([]Account, 0)
it := trie.NewIterator(st.NodeIterator(nil))
for it.Next() {
// Decode the state account
var data types.StateAccount
err := rlp.DecodeBytes(it.Value, &data)
if err != nil {
return fmt.Errorf("Failed to decode state account: %w", err)
}
// Check to see if the account has any code associated with it before performing
// more reads from the trie & db.
if bytes.Equal(data.CodeHash, emptyCodeHash) {
eoas++
continue
}
// Create a serializable `Account` object
account := Account{
Balance: data.Balance.String(),
Nonce: data.Nonce,
Root: data.Root[:],
CodeHash: data.CodeHash,
SecureKey: it.Key,
}
// Attempt to get the address of the account from the trie
addrBytes := st.Get(it.Key)
if addrBytes == nil {
// Preimage missing! Cannot continue.
missingPreimages++
continue
}
addr := common.BytesToAddress(addrBytes)
// Attempt to get the code of the account from the trie
code, err := stateDB.ContractCode(crypto.Keccak256Hash(addrBytes), common.BytesToHash(data.CodeHash))
if err != nil {
return fmt.Errorf("Could not load code for account %x: %w", addr, err)
}
// Check if the contract's runtime bytecode starts with the EOF prefix.
if len(code) >= 1 && code[0] == 0xEF {
// Append the account to the list of EOF contracts
account.Address = addr
account.Code = code
eofContracts = append(eofContracts, account)
} else {
nonEofContracts++
}
}
// Print finishing status
log.Printf("Indexing done in %v, found %d EOF contracts", time.Since(start), len(eofContracts))
log.Printf("Num missing preimages: %d", missingPreimages)
log.Printf("Non-EOF-prefixed contracts: %d", nonEofContracts)
log.Printf("Accounts with no code (EOAs): %d", eoas)
// Write the EOF contracts to a file
file, err := json.MarshalIndent(eofContracts, "", " ")
if err != nil {
return fmt.Errorf("Cannot marshal EOF contracts: %w", err)
}
err = os.WriteFile(out, file, 0644)
if err != nil {
return fmt.Errorf("Failed to write EOF contracts array to file: %w", err)
}
log.Printf("Wrote list of EOF contracts to `%v`", out)
return nil
}
package actions
import (
"context"
"github.com/ethereum/go-ethereum/log"
"github.com/stretchr/testify/require"
"github.com/ethereum-optimism/optimism/op-node/eth"
"github.com/ethereum-optimism/optimism/op-node/metrics"
"github.com/ethereum-optimism/optimism/op-node/rollup"
"github.com/ethereum-optimism/optimism/op-node/rollup/derive"
"github.com/ethereum-optimism/optimism/op-node/rollup/driver"
)
// MockL1OriginSelector is a shim to override the origin as sequencer, so we can force it to stay on an older origin.
type MockL1OriginSelector struct {
actual *driver.L1OriginSelector
originOverride eth.L1BlockRef // override which origin gets picked
}
func (m *MockL1OriginSelector) FindL1Origin(ctx context.Context, l1Head eth.L1BlockRef, l2Head eth.L2BlockRef) (eth.L1BlockRef, error) {
if m.originOverride != (eth.L1BlockRef{}) {
return m.originOverride, nil
}
return m.actual.FindL1Origin(ctx, l1Head, l2Head)
}
// L2Sequencer is an actor that functions like a rollup node,
// without the full P2P/API/Node stack, but just the derivation state, and simplified driver with sequencing ability.
type L2Sequencer struct {
L2Verifier
sequencer *driver.Sequencer
l1OriginSelector *driver.L1OriginSelector
seqOldOrigin bool // stay on current L1 origin when sequencing a block, unless forced to adopt the next origin
failL2GossipUnsafeBlock error // mock error
mockL1OriginSelector *MockL1OriginSelector
}
func NewL2Sequencer(t Testing, log log.Logger, l1 derive.L1Fetcher, eng L2API, cfg *rollup.Config, seqConfDepth uint64) *L2Sequencer {
ver := NewL2Verifier(t, log, l1, eng, cfg)
attrBuilder := derive.NewFetchingAttributesBuilder(cfg, l1, eng)
l1OriginSelector := &MockL1OriginSelector{
actual: driver.NewL1OriginSelector(log, cfg, l1, seqConfDepth),
}
return &L2Sequencer{
L2Verifier: *ver,
sequencer: driver.NewSequencer(log, cfg, eng, ver.derivation, attrBuilder, metrics.NoopMetrics),
l1OriginSelector: driver.NewL1OriginSelector(log, cfg, l1, seqConfDepth),
seqOldOrigin: false,
sequencer: driver.NewSequencer(log, cfg, ver.derivation, attrBuilder, l1OriginSelector),
mockL1OriginSelector: l1OriginSelector,
failL2GossipUnsafeBlock: nil,
}
}
......@@ -47,22 +62,7 @@ func (s *L2Sequencer) ActL2StartBlock(t Testing) {
return
}
parent := s.derivation.UnsafeL2Head()
var origin eth.L1BlockRef
if s.seqOldOrigin {
// force old origin, for testing purposes
oldOrigin, err := s.l1.L1BlockRefByHash(t.Ctx(), parent.L1Origin.Hash)
require.NoError(t, err, "failed to get current origin: %s", parent.L1Origin)
origin = oldOrigin
s.seqOldOrigin = false // don't repeat this
} else {
// select origin the real way
l1Origin, err := s.l1OriginSelector.FindL1Origin(t.Ctx(), s.l1State.L1Head(), parent)
require.NoError(t, err)
origin = l1Origin
}
err := s.sequencer.StartBuildingBlock(t.Ctx(), origin)
err := s.sequencer.StartBuildingBlock(t.Ctx(), s.l1State.L1Head())
require.NoError(t, err, "failed to start block building")
s.l2Building = true
......@@ -76,24 +76,21 @@ func (s *L2Sequencer) ActL2EndBlock(t Testing) {
}
s.l2Building = false
payload, err := s.sequencer.CompleteBuildingBlock(t.Ctx())
_, err := s.sequencer.CompleteBuildingBlock(t.Ctx())
// TODO: there may be legitimate temporary errors here, if we mock engine API RPC-failure.
// For advanced tests we can catch those and print a warning instead.
require.NoError(t, err)
ref, err := derive.PayloadToBlockRef(payload, &s.rollupCfg.Genesis)
require.NoError(t, err, "payload must convert to block ref")
s.derivation.SetUnsafeHead(ref)
// TODO: action-test publishing of payload on p2p
}
// ActL2KeepL1Origin makes the sequencer use the current L1 origin, even if the next origin is available.
func (s *L2Sequencer) ActL2KeepL1Origin(t Testing) {
if s.seqOldOrigin { // don't do this twice
t.InvalidAction("already decided to keep old L1 origin")
return
}
s.seqOldOrigin = true
parent := s.derivation.UnsafeL2Head()
// force old origin, for testing purposes
oldOrigin, err := s.l1.L1BlockRefByHash(t.Ctx(), parent.L1Origin.Hash)
require.NoError(t, err, "failed to get current origin: %s", parent.L1Origin)
s.mockL1OriginSelector.originOverride = oldOrigin
}
// ActBuildToL1Head builds empty blocks until (incl.) the L1 head becomes the L2 origin
......@@ -109,7 +106,7 @@ func (s *L2Sequencer) ActBuildToL1Head(t Testing) {
func (s *L2Sequencer) ActBuildToL1HeadExcl(t Testing) {
for {
s.ActL2PipelineFull(t)
nextOrigin, err := s.l1OriginSelector.FindL1Origin(t.Ctx(), s.l1State.L1Head(), s.derivation.UnsafeL2Head())
nextOrigin, err := s.mockL1OriginSelector.FindL1Origin(t.Ctx(), s.l1State.L1Head(), s.derivation.UnsafeL2Head())
require.NoError(t, err)
if nextOrigin.Number >= s.l1State.L1Head().Number {
break
......
......@@ -123,7 +123,7 @@ func RollupNodeMain(ctx *cli.Context) error {
if cfg.Heartbeat.Enabled {
var peerID string
if cfg.P2P == nil {
if cfg.P2P.Disabled() {
peerID = "disabled"
} else {
peerID = n.P2P().Host().ID().String()
......
......@@ -33,6 +33,7 @@ var DefaultBootnodes = []*enode.Node{
// SetupP2P provides a host and discovery service for usage in the rollup node.
type SetupP2P interface {
Check() error
Disabled() bool
// Host creates a libp2p host service. Returns nil, nil if p2p is disabled.
Host(log log.Logger, reporter metrics.Reporter) (host.Host, error)
// Discovery creates a disc-v5 service. Returns nil, nil, nil if discovery is disabled.
......@@ -134,6 +135,10 @@ func (conf *Config) TargetPeers() uint {
return conf.PeersLo
}
func (conf *Config) Disabled() bool {
return conf.DisableP2P
}
const maxMeshParam = 1000
func (conf *Config) Check() error {
......
......@@ -63,3 +63,7 @@ func (p *Prepared) Discovery(log log.Logger, rollupCfg *rollup.Config, tcpPort u
func (p *Prepared) ConfigureGossip(params *pubsub.GossipSubParams) []pubsub.Option {
return nil
}
func (p *Prepared) Disabled() bool {
return false
}
......@@ -33,6 +33,31 @@ type Engine interface {
SystemConfigL2Fetcher
}
// EngineState provides a read-only interface of the forkchoice state properties of the L2 Engine.
type EngineState interface {
Finalized() eth.L2BlockRef
UnsafeL2Head() eth.L2BlockRef
SafeL2Head() eth.L2BlockRef
}
// EngineControl enables other components to build blocks with the Engine,
// while keeping the forkchoice state and payload-id management internal to
// avoid state inconsistencies between different users of the EngineControl.
type EngineControl interface {
EngineState
// StartPayload requests the engine to start building a block with the given attributes.
// If updateSafe, the resulting block will be marked as a safe block.
StartPayload(ctx context.Context, parent eth.L2BlockRef, attrs *eth.PayloadAttributes, updateSafe bool) (errType BlockInsertionErrType, err error)
// ConfirmPayload requests the engine to complete the current block. If no block is being built, or if it fails, an error is returned.
ConfirmPayload(ctx context.Context) (out *eth.ExecutionPayload, errTyp BlockInsertionErrType, err error)
// CancelPayload requests the engine to stop building the current block without making it canonical.
// This is optional, as the engine expires building jobs that are left uncompleted, but can still save resources.
CancelPayload(ctx context.Context, force bool) error
// BuildingPayload indicates if a payload is being built, and onto which block it is being built, and whether or not it is a safe payload.
BuildingPayload() (onto eth.L2BlockRef, id eth.PayloadID, safe bool)
}
// Max memory used for buffering unsafe payloads
const maxUnsafePayloadsMemory = 500 * 1024 * 1024
......@@ -68,6 +93,10 @@ type EngineQueue struct {
safeHead eth.L2BlockRef
unsafeHead eth.L2BlockRef
buildingOnto eth.L2BlockRef
buildingID eth.PayloadID
buildingSafe bool
// Track when the rollup node changes the forkchoice without engine action,
// e.g. on a reset after a reorg, or after consolidating a block.
// This update may repeat if the engine returns a temporary error.
......@@ -91,6 +120,8 @@ type EngineQueue struct {
l1Fetcher L1Fetcher
}
var _ EngineControl = (*EngineQueue)(nil)
// NewEngineQueue creates a new EngineQueue, which should be Reset(origin) before use.
func NewEngineQueue(log log.Logger, cfg *rollup.Config, engine Engine, metrics Metrics, prev NextAttributesProvider, l1Fetcher L1Fetcher) *EngineQueue {
return &EngineQueue{
......@@ -416,13 +447,11 @@ func (eq *EngineQueue) forceNextSafeAttributes(ctx context.Context) error {
if len(eq.safeAttributes) == 0 {
return nil
}
fc := eth.ForkchoiceState{
HeadBlockHash: eq.safeHead.Hash,
SafeBlockHash: eq.safeHead.Hash,
FinalizedBlockHash: eq.finalized.Hash,
}
attrs := eq.safeAttributes[0]
payload, errType, err := InsertHeadBlock(ctx, eq.log, eq.engine, fc, attrs, true)
errType, err := eq.StartPayload(ctx, eq.safeHead, attrs, true)
if err == nil {
_, errType, err = eq.ConfirmPayload(ctx)
}
if err != nil {
switch errType {
case BlockInsertTemporaryErr:
......@@ -457,21 +486,89 @@ func (eq *EngineQueue) forceNextSafeAttributes(ctx context.Context) error {
return NewCriticalError(fmt.Errorf("unknown InsertHeadBlock error type %d: %w", errType, err))
}
}
eq.safeAttributes = eq.safeAttributes[1:]
eq.logSyncProgress("processed safe block derived from L1")
return nil
}
func (eq *EngineQueue) StartPayload(ctx context.Context, parent eth.L2BlockRef, attrs *eth.PayloadAttributes, updateSafe bool) (errType BlockInsertionErrType, err error) {
if eq.buildingID != (eth.PayloadID{}) {
eq.log.Warn("did not finish previous block building, starting new building now", "prev_onto", eq.buildingOnto, "prev_payload_id", eq.buildingID, "new_onto", parent)
// TODO: maybe worth it to force-cancel the old payload ID here.
}
fc := eth.ForkchoiceState{
HeadBlockHash: parent.Hash,
SafeBlockHash: eq.safeHead.Hash,
FinalizedBlockHash: eq.finalized.Hash,
}
id, errTyp, err := StartPayload(ctx, eq.engine, fc, attrs)
if err != nil {
return errTyp, err
}
eq.buildingID = id
eq.buildingSafe = updateSafe
eq.buildingOnto = parent
return BlockInsertOK, nil
}
func (eq *EngineQueue) ConfirmPayload(ctx context.Context) (out *eth.ExecutionPayload, errTyp BlockInsertionErrType, err error) {
if eq.buildingID == (eth.PayloadID{}) {
return nil, BlockInsertPrestateErr, fmt.Errorf("cannot complete payload building: not currently building a payload")
}
if eq.buildingOnto.Hash != eq.unsafeHead.Hash { // E.g. when safe-attributes consolidation fails, it will drop the existing work.
eq.log.Warn("engine is building block that reorgs previous usafe head", "onto", eq.buildingOnto, "unsafe", eq.unsafeHead)
}
fc := eth.ForkchoiceState{
HeadBlockHash: common.Hash{}, // gets overridden
SafeBlockHash: eq.safeHead.Hash,
FinalizedBlockHash: eq.finalized.Hash,
}
payload, errTyp, err := ConfirmPayload(ctx, eq.log, eq.engine, fc, eq.buildingID, eq.buildingSafe)
if err != nil {
return nil, errTyp, fmt.Errorf("failed to complete building on top of L2 chain %s, id: %s, error (%d): %w", eq.buildingOnto, eq.buildingID, errTyp, err)
}
ref, err := PayloadToBlockRef(payload, &eq.cfg.Genesis)
if err != nil {
return NewTemporaryError(fmt.Errorf("failed to decode L2 block ref from payload: %w", err))
return nil, BlockInsertPayloadErr, NewResetError(fmt.Errorf("failed to decode L2 block ref from payload: %w", err))
}
eq.safeHead = ref
eq.unsafeHead = ref
eq.metrics.RecordL2Ref("l2_safe", ref)
eq.metrics.RecordL2Ref("l2_unsafe", ref)
eq.safeAttributes = eq.safeAttributes[1:]
if eq.buildingSafe {
eq.safeHead = ref
eq.postProcessSafeL2()
eq.logSyncProgress("processed safe block derived from L1")
eq.metrics.RecordL2Ref("l2_safe", ref)
}
eq.resetBuildingState()
return payload, BlockInsertOK, nil
}
func (eq *EngineQueue) CancelPayload(ctx context.Context, force bool) error {
// the building job gets wrapped up as soon as the payload is retrieved, there's no explicit cancel in the Engine API
eq.log.Error("cancelling old block sealing job", "payload", eq.buildingID)
_, err := eq.engine.GetPayload(ctx, eq.buildingID)
if err != nil {
eq.log.Error("failed to cancel block building job", "payload", eq.buildingID, "err", err)
if !force {
return err
}
}
eq.resetBuildingState()
return nil
}
func (eq *EngineQueue) BuildingPayload() (onto eth.L2BlockRef, id eth.PayloadID, safe bool) {
return eq.buildingOnto, eq.buildingID, eq.buildingSafe
}
func (eq *EngineQueue) resetBuildingState() {
eq.buildingID = eth.PayloadID{}
eq.buildingOnto = eth.L2BlockRef{}
eq.buildingSafe = false
}
// ResetStep Walks the L2 chain backwards until it finds an L2 block whose L1 origin is canonical.
// The unsafe head is set to the head of the L2 chain, unless the existing safe head is not canonical.
func (eq *EngineQueue) Reset(ctx context.Context, _ eth.L1BlockRef, _ eth.SystemConfig) error {
......@@ -517,6 +614,7 @@ func (eq *EngineQueue) Reset(ctx context.Context, _ eth.L1BlockRef, _ eth.System
eq.unsafeHead = unsafe
eq.safeHead = safe
eq.finalized = finalized
eq.resetBuildingState()
eq.needForkchoiceUpdate = true
eq.finalityData = eq.finalityData[:0]
// note: we do not clear the unsafe payloads queue; if the payloads are not applicable anymore the parent hash checks will clear out the old payloads.
......
......@@ -79,19 +79,6 @@ const (
BlockInsertPayloadErr
)
// InsertHeadBlock creates, executes, and inserts the specified block as the head block.
// It first uses the given FC to start the block creation process and then after the payload is executed,
// sets the FC to the same safe and finalized hashes, but updates the head hash to the new block.
// If updateSafe is true, the head block is considered to be the safe head as well as the head.
// It returns the payload, an RPC error (if the payload might still be valid), and a payload error (if the payload was not valid)
func InsertHeadBlock(ctx context.Context, log log.Logger, eng Engine, fc eth.ForkchoiceState, attrs *eth.PayloadAttributes, updateSafe bool) (out *eth.ExecutionPayload, errTyp BlockInsertionErrType, err error) {
id, errTyp, err := StartPayload(ctx, eng, fc, attrs)
if err != nil {
return nil, errTyp, err
}
return ConfirmPayload(ctx, log, eng, fc, id, updateSafe)
}
// StartPayload starts an execution payload building process in the provided Engine, with the given attributes.
// The severity of the error is distinguished to determine whether the same payload attributes may be re-attempted later.
func StartPayload(ctx context.Context, eng Engine, fc eth.ForkchoiceState, attrs *eth.PayloadAttributes) (id eth.PayloadID, errType BlockInsertionErrType, err error) {
......
......@@ -31,6 +31,8 @@ type ResetableStage interface {
}
type EngineQueueStage interface {
EngineControl
FinalizedL1() eth.L1BlockRef
Finalized() eth.L2BlockRef
UnsafeL2Head() eth.L2BlockRef
......@@ -130,8 +132,20 @@ func (dp *DerivationPipeline) UnsafeL2Head() eth.L2BlockRef {
return dp.eng.UnsafeL2Head()
}
func (dp *DerivationPipeline) SetUnsafeHead(head eth.L2BlockRef) {
dp.eng.SetUnsafeHead(head)
func (dp *DerivationPipeline) StartPayload(ctx context.Context, parent eth.L2BlockRef, attrs *eth.PayloadAttributes, updateSafe bool) (errType BlockInsertionErrType, err error) {
return dp.eng.StartPayload(ctx, parent, attrs, updateSafe)
}
func (dp *DerivationPipeline) ConfirmPayload(ctx context.Context) (out *eth.ExecutionPayload, errTyp BlockInsertionErrType, err error) {
return dp.eng.ConfirmPayload(ctx)
}
func (dp *DerivationPipeline) CancelPayload(ctx context.Context, force bool) error {
return dp.eng.CancelPayload(ctx, force)
}
func (dp *DerivationPipeline) BuildingPayload() (onto eth.L2BlockRef, id eth.PayloadID, safe bool) {
return dp.eng.BuildingPayload()
}
// AddUnsafePayload schedules an execution payload to be processed, ahead of deriving it from L1
......
......@@ -14,7 +14,6 @@ import (
type Metrics interface {
RecordPipelineReset()
RecordSequencingError()
RecordPublishingError()
RecordDerivationError()
......@@ -28,9 +27,8 @@ type Metrics interface {
SetDerivationIdle(idle bool)
RecordL1ReorgDepth(d uint64)
CountSequencedTxs(count int)
SequencerMetrics
EngineMetrics
}
type L1Chain interface {
......@@ -48,7 +46,6 @@ type L2Chain interface {
type DerivationPipeline interface {
Reset()
Step(ctx context.Context) error
SetUnsafeHead(head eth.L2BlockRef)
AddUnsafePayload(payload *eth.ExecutionPayload)
Finalize(ref eth.L1BlockRef)
FinalizedL1() eth.L1BlockRef
......@@ -68,14 +65,12 @@ type L1StateIface interface {
L1Finalized() eth.L1BlockRef
}
type L1OriginSelectorIface interface {
FindL1Origin(ctx context.Context, l1Head eth.L1BlockRef, l2Head eth.L2BlockRef) (eth.L1BlockRef, error)
}
type SequencerIface interface {
StartBuildingBlock(ctx context.Context, l1Origin eth.L1BlockRef) error
StartBuildingBlock(ctx context.Context, l1Head eth.L1BlockRef) error
CompleteBuildingBlock(ctx context.Context) (*eth.ExecutionPayload, error)
PlanNextSequencerAction(sequenceErr error) (delay time.Duration, seal bool, onto eth.BlockID)
PlanNextSequencerAction() time.Duration
RunNextSequencerAction(ctx context.Context, l1Head eth.L1BlockRef) *eth.ExecutionPayload
BuildingOnto() eth.L2BlockRef
}
type Network interface {
......@@ -90,7 +85,10 @@ func NewDriver(driverCfg *Config, cfg *rollup.Config, l2 L2Chain, l1 L1Chain, ne
verifConfDepth := NewConfDepth(driverCfg.VerifierConfDepth, l1State.L1Head, l1)
derivationPipeline := derive.NewDerivationPipeline(log, cfg, verifConfDepth, l2, metrics)
attrBuilder := derive.NewFetchingAttributesBuilder(cfg, l1, l2)
sequencer := NewSequencer(log, cfg, l2, derivationPipeline, attrBuilder, metrics)
engine := derivationPipeline
meteredEngine := NewMeteredEngine(cfg, engine, metrics, log)
sequencer := NewSequencer(log, cfg, meteredEngine, attrBuilder, findL1Origin)
return &Driver{
l1State: l1State,
derivation: derivationPipeline,
......@@ -106,7 +104,6 @@ func NewDriver(driverCfg *Config, cfg *rollup.Config, l2 L2Chain, l1 L1Chain, ne
snapshotLog: snapshotLog,
l1: l1,
l2: l2,
l1OriginSelector: findL1Origin,
sequencer: sequencer,
network: network,
metrics: metrics,
......
package driver
import (
"context"
"time"
"github.com/ethereum/go-ethereum/log"
"github.com/ethereum-optimism/optimism/op-node/eth"
"github.com/ethereum-optimism/optimism/op-node/rollup"
"github.com/ethereum-optimism/optimism/op-node/rollup/derive"
)
type EngineMetrics interface {
RecordSequencingError()
CountSequencedTxs(count int)
RecordSequencerBuildingDiffTime(duration time.Duration)
RecordSequencerSealingTime(duration time.Duration)
}
// MeteredEngine wraps an EngineControl and adds metrics such as block building time diff and sealing time
type MeteredEngine struct {
inner derive.EngineControl
cfg *rollup.Config
metrics EngineMetrics
log log.Logger
buildingStartTime time.Time
}
// MeteredEngine implements derive.EngineControl
var _ derive.EngineControl = (*MeteredEngine)(nil)
func NewMeteredEngine(cfg *rollup.Config, inner derive.EngineControl, metrics EngineMetrics, log log.Logger) *MeteredEngine {
return &MeteredEngine{
inner: inner,
cfg: cfg,
metrics: metrics,
log: log,
}
}
func (m *MeteredEngine) Finalized() eth.L2BlockRef {
return m.inner.Finalized()
}
func (m *MeteredEngine) UnsafeL2Head() eth.L2BlockRef {
return m.inner.UnsafeL2Head()
}
func (m *MeteredEngine) SafeL2Head() eth.L2BlockRef {
return m.inner.SafeL2Head()
}
func (m *MeteredEngine) StartPayload(ctx context.Context, parent eth.L2BlockRef, attrs *eth.PayloadAttributes, updateSafe bool) (errType derive.BlockInsertionErrType, err error) {
m.buildingStartTime = time.Now()
errType, err = m.inner.StartPayload(ctx, parent, attrs, updateSafe)
if err != nil {
m.metrics.RecordSequencingError()
}
return errType, err
}
func (m *MeteredEngine) ConfirmPayload(ctx context.Context) (out *eth.ExecutionPayload, errTyp derive.BlockInsertionErrType, err error) {
sealingStart := time.Now()
// Actually execute the block and add it to the head of the chain.
payload, errType, err := m.inner.ConfirmPayload(ctx)
if err != nil {
m.metrics.RecordSequencingError()
return payload, errType, err
}
now := time.Now()
sealTime := now.Sub(sealingStart)
buildTime := now.Sub(m.buildingStartTime)
m.metrics.RecordSequencerSealingTime(sealTime)
m.metrics.RecordSequencerBuildingDiffTime(buildTime - time.Duration(m.cfg.BlockTime)*time.Second)
m.metrics.CountSequencedTxs(len(payload.Transactions))
ref := m.inner.UnsafeL2Head()
m.log.Debug("Processed new L2 block", "l2_unsafe", ref, "l1_origin", ref.L1Origin,
"txs", len(payload.Transactions), "time", ref.Time, "seal_time", sealTime, "build_time", buildTime)
return payload, errType, err
}
func (m *MeteredEngine) CancelPayload(ctx context.Context, force bool) error {
return m.inner.CancelPayload(ctx, force)
}
func (m *MeteredEngine) BuildingPayload() (onto eth.L2BlockRef, id eth.PayloadID, safe bool) {
return m.inner.BuildingPayload()
}
......@@ -2,7 +2,6 @@ package driver
import (
"context"
"errors"
"fmt"
"time"
......@@ -20,16 +19,8 @@ type Downloader interface {
FetchReceipts(ctx context.Context, blockHash common.Hash) (eth.BlockInfo, types.Receipts, error)
}
type SequencerMetrics interface {
RecordSequencerBuildingDiffTime(duration time.Duration)
RecordSequencerSealingTime(duration time.Duration)
}
type EngineState interface {
Finalized() eth.L2BlockRef
UnsafeL2Head() eth.L2BlockRef
SafeL2Head() eth.L2BlockRef
Origin() eth.L1BlockRef
type L1OriginSelectorIface interface {
FindL1Origin(ctx context.Context, l1Head eth.L1BlockRef, l2Head eth.L2BlockRef) (eth.L1BlockRef, error)
}
// Sequencer implements the sequencing interface of the driver: it starts and completes block building jobs.
......@@ -37,40 +28,44 @@ type Sequencer struct {
log log.Logger
config *rollup.Config
l2 derive.Engine
engineState EngineState
engine derive.EngineControl
attrBuilder derive.AttributesBuilder
buildingOnto eth.L2BlockRef
buildingID eth.PayloadID
buildingStartTime time.Time
l1OriginSelector L1OriginSelectorIface
// timeNow enables sequencer testing to mock the time
timeNow func() time.Time
metrics SequencerMetrics
nextAction time.Time
}
func NewSequencer(log log.Logger, cfg *rollup.Config, l2 derive.Engine, engineState EngineState, attributesBuilder derive.AttributesBuilder, metrics SequencerMetrics) *Sequencer {
func NewSequencer(log log.Logger, cfg *rollup.Config, engine derive.EngineControl, attributesBuilder derive.AttributesBuilder, l1OriginSelector L1OriginSelectorIface) *Sequencer {
return &Sequencer{
log: log,
config: cfg,
l2: l2,
metrics: metrics,
engineState: engineState,
engine: engine,
timeNow: time.Now,
attrBuilder: attributesBuilder,
l1OriginSelector: l1OriginSelector,
}
}
// StartBuildingBlock initiates a block building job on top of the given L2 head, safe and finalized blocks, and using the provided l1Origin.
func (d *Sequencer) StartBuildingBlock(ctx context.Context, l1Origin eth.L1BlockRef) error {
l2Head := d.engineState.UnsafeL2Head()
func (d *Sequencer) StartBuildingBlock(ctx context.Context, l1Head eth.L1BlockRef) error {
l2Head := d.engine.UnsafeL2Head()
// Figure out which L1 origin block we're going to be building on top of.
l1Origin, err := d.l1OriginSelector.FindL1Origin(ctx, l1Head, l2Head)
if err != nil {
d.log.Error("Error finding next L1 Origin", "err", err)
return err
}
if !(l2Head.L1Origin.Hash == l1Origin.ParentHash || l2Head.L1Origin.Hash == l1Origin.Hash) {
return fmt.Errorf("cannot build new L2 block with L1 origin %s (parent L1 %s) on current L2 head %s with L1 origin %s", l1Origin, l1Origin.ParentHash, l2Head, l2Head.L1Origin)
}
d.log.Info("creating new block", "parent", l2Head, "l1Origin", l1Origin)
if d.buildingID != (eth.PayloadID{}) { // This may happen when we decide to build a different block in response to a reorg. Or when previous block building failed.
d.log.Warn("did not finish previous block building, starting new building now", "prev_onto", d.buildingOnto, "prev_payload_id", d.buildingID, "new_onto", l2Head)
}
d.buildingStartTime = time.Now()
fetchCtx, cancel := context.WithTimeout(ctx, time.Second*20)
defer cancel()
......@@ -90,20 +85,11 @@ func (d *Sequencer) StartBuildingBlock(ctx context.Context, l1Origin eth.L1Block
"num", l2Head.Number+1, "time", uint64(attrs.Timestamp),
"origin", l1Origin, "origin_time", l1Origin.Time, "noTxPool", attrs.NoTxPool)
// And construct our fork choice state. This is our current fork choice state and will be
// updated as a result of executing the block based on the attributes described above.
fc := eth.ForkchoiceState{
HeadBlockHash: l2Head.Hash,
SafeBlockHash: d.engineState.SafeL2Head().Hash,
FinalizedBlockHash: d.engineState.Finalized().Hash,
}
// Start a payload building process.
id, errTyp, err := derive.StartPayload(ctx, d.l2, fc, attrs)
errTyp, err := d.engine.StartPayload(ctx, l2Head, attrs, false)
if err != nil {
return fmt.Errorf("failed to start building on top of L2 chain %s, error (%d): %w", l2Head, errTyp, err)
}
d.buildingOnto = l2Head
d.buildingID = id
return nil
}
......@@ -111,75 +97,90 @@ func (d *Sequencer) StartBuildingBlock(ctx context.Context, l1Origin eth.L1Block
// Warning: the safe and finalized L2 blocks as viewed during the initiation of the block building are reused for completion of the block building.
// The Execution engine should not change the safe and finalized blocks between start and completion of block building.
func (d *Sequencer) CompleteBuildingBlock(ctx context.Context) (*eth.ExecutionPayload, error) {
if d.buildingID == (eth.PayloadID{}) {
return nil, fmt.Errorf("cannot complete payload building: not currently building a payload")
}
sealingStart := time.Now()
l2Head := d.engineState.UnsafeL2Head()
if d.buildingOnto.Hash != l2Head.Hash {
return nil, fmt.Errorf("engine reorged from %s to %s while building block", d.buildingOnto, l2Head)
}
fc := eth.ForkchoiceState{
HeadBlockHash: l2Head.Hash,
SafeBlockHash: d.engineState.SafeL2Head().Hash,
FinalizedBlockHash: d.engineState.Finalized().Hash,
}
// Actually execute the block and add it to the head of the chain.
payload, errTyp, err := derive.ConfirmPayload(ctx, d.log, d.l2, fc, d.buildingID, false)
payload, errTyp, err := d.engine.ConfirmPayload(ctx)
if err != nil {
return nil, fmt.Errorf("failed to complete building on top of L2 chain %s, id: %s, error (%d): %w", d.buildingOnto, d.buildingID, errTyp, err)
return nil, fmt.Errorf("failed to complete building block: error (%d): %w", errTyp, err)
}
now := time.Now()
sealTime := now.Sub(sealingStart)
buildTime := now.Sub(d.buildingStartTime)
d.metrics.RecordSequencerSealingTime(sealTime)
d.metrics.RecordSequencerBuildingDiffTime(buildTime - time.Duration(d.config.BlockTime)*time.Second)
d.log.Debug("sequenced block", "seal_time", sealTime, "build_time", buildTime)
d.buildingID = eth.PayloadID{}
return payload, nil
}
// PlanNextSequencerAction returns a desired delay till the next action, and if we should seal the block:
// - true whenever we need to complete a block
// - false whenever we need to start a block
func (d *Sequencer) PlanNextSequencerAction(sequenceErr error) (delay time.Duration, seal bool, onto eth.BlockID) {
blockTime := time.Duration(d.config.BlockTime) * time.Second
head := d.engineState.UnsafeL2Head()
// CancelBuildingBlock cancels the current open block building job.
// This sequencer only maintains one block building job at a time.
func (d *Sequencer) CancelBuildingBlock(ctx context.Context) {
// force-cancel, we can always continue block building, and any error is logged by the engine state
_ = d.engine.CancelPayload(ctx, true)
}
// based on the build error, delay and start over again
if sequenceErr != nil {
if errors.Is(sequenceErr, UninitializedL1StateErr) {
// temporary errors are not so bad, just retry in 500ms
return 500 * time.Millisecond, false, head.ID()
} else {
// we just hit an unknown type of error, delay a re-attempt by as much as a block
return blockTime, false, head.ID()
}
// PlanNextSequencerAction returns a desired delay till the RunNextSequencerAction call.
func (d *Sequencer) PlanNextSequencerAction() time.Duration {
head := d.engine.UnsafeL2Head()
now := d.timeNow()
buildingOnto, buildingID, _ := d.engine.BuildingPayload()
// We may have to wait till the next sequencing action, e.g. upon an error.
// If the head changed we need to respond and will not delay the sequencing.
if delay := d.nextAction.Sub(now); delay > 0 && buildingOnto.Hash == head.Hash {
return delay
}
blockTime := time.Duration(d.config.BlockTime) * time.Second
payloadTime := time.Unix(int64(head.Time+d.config.BlockTime), 0)
remainingTime := time.Until(payloadTime)
remainingTime := payloadTime.Sub(now)
// If we started building a block already, and if that work is still consistent,
// then we would like to finish it by sealing the block.
if d.buildingID != (eth.PayloadID{}) && d.buildingOnto.Hash == head.Hash {
if buildingID != (eth.PayloadID{}) && buildingOnto.Hash == head.Hash {
// if we started building already, then we will schedule the sealing.
if remainingTime < sealingDuration {
return 0, true, head.ID() // if there's not enough time for sealing, don't wait.
return 0 // if there's not enough time for sealing, don't wait.
} else {
// finish with margin of sealing duration before payloadTime
return remainingTime - sealingDuration, true, head.ID()
return remainingTime - sealingDuration
}
} else {
// if we did not yet start building, then we will schedule the start.
if remainingTime > blockTime {
// if we have too much time, then wait before starting the build
return remainingTime - blockTime, false, head.ID()
return remainingTime - blockTime
} else {
// otherwise start instantly
return 0, false, head.ID()
return 0
}
}
}
// BuildingOnto returns the L2 head reference that the latest block is or was being built on top of.
func (d *Sequencer) BuildingOnto() eth.L2BlockRef {
ref, _, _ := d.engine.BuildingPayload()
return ref
}
// RunNextSequencerAction starts new block building work, or seals existing work,
// and is best timed by first awaiting the delay returned by PlanNextSequencerAction.
// If a new block is successfully sealed, it will be returned for publishing, nil otherwise.
func (d *Sequencer) RunNextSequencerAction(ctx context.Context, l1Head eth.L1BlockRef) *eth.ExecutionPayload {
if _, buildingID, _ := d.engine.BuildingPayload(); buildingID != (eth.PayloadID{}) {
payload, err := d.CompleteBuildingBlock(ctx)
if err != nil {
d.log.Error("sequencer failed to seal new block", "err", err)
d.nextAction = d.timeNow().Add(time.Second)
if buildingID != (eth.PayloadID{}) { // don't keep stale block building jobs around, try to cancel them
d.CancelBuildingBlock(ctx)
}
return nil
} else {
d.log.Info("sequencer successfully built a new block", "block", payload.ID(), "time", uint64(payload.Timestamp), "txs", len(payload.Transactions))
return payload
}
} else {
err := d.StartBuildingBlock(ctx, l1Head)
if err != nil {
d.log.Error("sequencer failed to start building new block", "err", err)
d.nextAction = d.timeNow().Add(time.Second)
} else {
d.log.Info("sequencer started building new block", "payload_id", buildingID)
}
return nil
}
}
package driver
import (
"context"
crand "crypto/rand"
"encoding/binary"
"errors"
"fmt"
"math/big"
"math/rand"
"testing"
"time"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/log"
"github.com/stretchr/testify/require"
"github.com/ethereum-optimism/optimism/op-node/eth"
"github.com/ethereum-optimism/optimism/op-node/rollup"
"github.com/ethereum-optimism/optimism/op-node/rollup/derive"
"github.com/ethereum-optimism/optimism/op-node/testlog"
"github.com/ethereum-optimism/optimism/op-node/testutils"
)
type FakeEngineControl struct {
finalized eth.L2BlockRef
safe eth.L2BlockRef
unsafe eth.L2BlockRef
buildingOnto eth.L2BlockRef
buildingID eth.PayloadID
buildingSafe bool
buildingAttrs *eth.PayloadAttributes
buildingStart time.Time
cfg *rollup.Config
timeNow func() time.Time
makePayload func(onto eth.L2BlockRef, attrs *eth.PayloadAttributes) *eth.ExecutionPayload
errTyp derive.BlockInsertionErrType
err error
totalBuildingTime time.Duration
totalBuiltBlocks int
totalTxs int
}
func (m *FakeEngineControl) avgBuildingTime() time.Duration {
return m.totalBuildingTime / time.Duration(m.totalBuiltBlocks)
}
func (m *FakeEngineControl) avgTxsPerBlock() float64 {
return float64(m.totalTxs) / float64(m.totalBuiltBlocks)
}
func (m *FakeEngineControl) StartPayload(ctx context.Context, parent eth.L2BlockRef, attrs *eth.PayloadAttributes, updateSafe bool) (errType derive.BlockInsertionErrType, err error) {
if m.err != nil {
return m.errTyp, m.err
}
m.buildingID = eth.PayloadID{}
_, _ = crand.Read(m.buildingID[:])
m.buildingOnto = parent
m.buildingSafe = updateSafe
m.buildingAttrs = attrs
m.buildingStart = m.timeNow()
return derive.BlockInsertOK, nil
}
func (m *FakeEngineControl) ConfirmPayload(ctx context.Context) (out *eth.ExecutionPayload, errTyp derive.BlockInsertionErrType, err error) {
if m.err != nil {
return nil, m.errTyp, m.err
}
buildTime := m.timeNow().Sub(m.buildingStart)
m.totalBuildingTime += buildTime
m.totalBuiltBlocks += 1
payload := m.makePayload(m.buildingOnto, m.buildingAttrs)
ref, err := derive.PayloadToBlockRef(payload, &m.cfg.Genesis)
if err != nil {
panic(err)
}
m.unsafe = ref
if m.buildingSafe {
m.safe = ref
}
m.resetBuildingState()
m.totalTxs += len(payload.Transactions)
return payload, derive.BlockInsertOK, nil
}
func (m *FakeEngineControl) CancelPayload(ctx context.Context, force bool) error {
if force {
m.resetBuildingState()
}
return m.err
}
func (m *FakeEngineControl) BuildingPayload() (onto eth.L2BlockRef, id eth.PayloadID, safe bool) {
return m.buildingOnto, m.buildingID, m.buildingSafe
}
func (m *FakeEngineControl) Finalized() eth.L2BlockRef {
return m.finalized
}
func (m *FakeEngineControl) UnsafeL2Head() eth.L2BlockRef {
return m.unsafe
}
func (m *FakeEngineControl) SafeL2Head() eth.L2BlockRef {
return m.safe
}
func (m *FakeEngineControl) resetBuildingState() {
m.buildingID = eth.PayloadID{}
m.buildingOnto = eth.L2BlockRef{}
m.buildingSafe = false
m.buildingAttrs = nil
}
var _ derive.EngineControl = (*FakeEngineControl)(nil)
type testAttrBuilderFn func(ctx context.Context, l2Parent eth.L2BlockRef, epoch eth.BlockID) (attrs *eth.PayloadAttributes, err error)
func (fn testAttrBuilderFn) PreparePayloadAttributes(ctx context.Context, l2Parent eth.L2BlockRef, epoch eth.BlockID) (attrs *eth.PayloadAttributes, err error) {
return fn(ctx, l2Parent, epoch)
}
var _ derive.AttributesBuilder = (testAttrBuilderFn)(nil)
type testOriginSelectorFn func(ctx context.Context, l1Head eth.L1BlockRef, l2Head eth.L2BlockRef) (eth.L1BlockRef, error)
func (fn testOriginSelectorFn) FindL1Origin(ctx context.Context, l1Head eth.L1BlockRef, l2Head eth.L2BlockRef) (eth.L1BlockRef, error) {
return fn(ctx, l1Head, l2Head)
}
var _ L1OriginSelectorIface = (testOriginSelectorFn)(nil)
// TestSequencerChaosMonkey runs the sequencer in a mocked adversarial environment with
// repeated random errors in dependencies and poor clock timing.
// At the end the health of the chain is checked to show that the sequencer kept the chain in shape.
func TestSequencerChaosMonkey(t *testing.T) {
mockL1Hash := func(num uint64) (out common.Hash) {
out[31] = 1
binary.BigEndian.PutUint64(out[:], num)
return
}
mockL2Hash := func(num uint64) (out common.Hash) {
out[31] = 2
binary.BigEndian.PutUint64(out[:], num)
return
}
mockL1ID := func(num uint64) eth.BlockID {
return eth.BlockID{Hash: mockL1Hash(num), Number: num}
}
mockL2ID := func(num uint64) eth.BlockID {
return eth.BlockID{Hash: mockL2Hash(num), Number: num}
}
rng := rand.New(rand.NewSource(12345))
l1Time := uint64(100000)
// mute errors. We expect a lot of the mocked errors to cause error-logs. We check chain health at the end of the test.
log := testlog.Logger(t, log.LvlCrit)
cfg := &rollup.Config{
Genesis: rollup.Genesis{
L1: mockL1ID(100000),
L2: mockL2ID(200000),
L2Time: l1Time + 300, // L2 may start with a relative old L1 origin and will have to catch it up
SystemConfig: eth.SystemConfig{},
},
BlockTime: 2,
MaxSequencerDrift: 30,
}
// keep track of the L1 timestamps we mock because sometimes we only have the L1 hash/num handy
l1Times := map[eth.BlockID]uint64{cfg.Genesis.L1: l1Time}
genesisL2 := eth.L2BlockRef{
Hash: cfg.Genesis.L2.Hash,
Number: cfg.Genesis.L2.Number,
ParentHash: mockL2Hash(cfg.Genesis.L2.Number - 1),
Time: cfg.Genesis.L2Time,
L1Origin: cfg.Genesis.L1,
SequenceNumber: 0,
}
// initialize our engine state
engControl := &FakeEngineControl{
finalized: genesisL2,
safe: genesisL2,
unsafe: genesisL2,
cfg: cfg,
}
// start wallclock at 5 minutes after the current L2 head. The sequencer has some catching up to do!
clockTime := time.Unix(int64(engControl.unsafe.Time)+5*60, 0)
clockFn := func() time.Time {
return clockTime
}
engControl.timeNow = clockFn
// mock payload building, we don't need to process any real txs.
engControl.makePayload = func(onto eth.L2BlockRef, attrs *eth.PayloadAttributes) *eth.ExecutionPayload {
txs := make([]eth.Data, 0)
txs = append(txs, attrs.Transactions...) // include deposits
if !attrs.NoTxPool { // if we are allowed to sequence from tx pool, mock some txs
n := rng.Intn(20)
for i := 0; i < n; i++ {
txs = append(txs, []byte(fmt.Sprintf("mock sequenced tx %d", i)))
}
}
return &eth.ExecutionPayload{
ParentHash: onto.Hash,
BlockNumber: eth.Uint64Quantity(onto.Number) + 1,
Timestamp: attrs.Timestamp,
BlockHash: mockL2Hash(onto.Number),
Transactions: txs,
}
}
// We keep attribute building simple, we don't talk to a real execution engine in this test.
// Sometimes we fake an error in the attributes preparation.
var attrsErr error
attrBuilder := testAttrBuilderFn(func(ctx context.Context, l2Parent eth.L2BlockRef, epoch eth.BlockID) (attrs *eth.PayloadAttributes, err error) {
if attrsErr != nil {
return nil, attrsErr
}
seqNr := l2Parent.SequenceNumber + 1
if epoch != l2Parent.L1Origin {
seqNr = 0
}
l1Info := &testutils.MockBlockInfo{
InfoHash: epoch.Hash,
InfoParentHash: mockL1Hash(epoch.Number - 1),
InfoCoinbase: common.Address{},
InfoRoot: common.Hash{},
InfoNum: epoch.Number,
InfoTime: l1Times[epoch],
InfoMixDigest: [32]byte{},
InfoBaseFee: big.NewInt(1234),
InfoReceiptRoot: common.Hash{},
}
infoDep, err := derive.L1InfoDepositBytes(seqNr, l1Info, cfg.Genesis.SystemConfig)
require.NoError(t, err)
testGasLimit := eth.Uint64Quantity(10_000_000)
return &eth.PayloadAttributes{
Timestamp: eth.Uint64Quantity(l2Parent.Time + cfg.BlockTime),
PrevRandao: eth.Bytes32{},
SuggestedFeeRecipient: common.Address{},
Transactions: []eth.Data{infoDep},
NoTxPool: false,
GasLimit: &testGasLimit,
}, nil
})
maxL1BlockTimeGap := uint64(100)
// The origin selector just generates random L1 blocks based on RNG
var originErr error
originSelector := testOriginSelectorFn(func(ctx context.Context, l1Head eth.L1BlockRef, l2Head eth.L2BlockRef) (eth.L1BlockRef, error) {
if originErr != nil {
return eth.L1BlockRef{}, originErr
}
origin := eth.L1BlockRef{
Hash: mockL1Hash(l2Head.L1Origin.Number),
Number: l2Head.L1Origin.Number,
ParentHash: mockL1Hash(l2Head.L1Origin.Number),
Time: l1Times[l2Head.L1Origin],
}
// randomly make a L1 origin appear, if we can even select it
nextL2Time := l2Head.Time + cfg.BlockTime
if nextL2Time <= origin.Time {
return origin, nil
}
maxTimeIncrement := nextL2Time - origin.Time
if maxTimeIncrement > maxL1BlockTimeGap {
maxTimeIncrement = maxL1BlockTimeGap
}
if rng.Intn(10) == 0 {
nextOrigin := eth.L1BlockRef{
Hash: mockL1Hash(origin.Number + 1),
Number: origin.Number + 1,
ParentHash: origin.Hash,
Time: origin.Time + 1 + uint64(rng.Int63n(int64(maxTimeIncrement))),
}
l1Times[nextOrigin.ID()] = nextOrigin.Time
return nextOrigin, nil
} else {
return origin, nil
}
})
seq := NewSequencer(log, cfg, engControl, attrBuilder, originSelector)
seq.timeNow = clockFn
l1Head := eth.L1BlockRef{} // TODO this is getting removed
// try to build 1000 blocks, with 5x as many planning attempts, to handle errors and clock problems
desiredBlocks := 1000
for i := 0; i < 5*desiredBlocks && engControl.totalBuiltBlocks < desiredBlocks; i++ {
delta := seq.PlanNextSequencerAction()
x := rng.Float32()
if x < 0.01 { // 1%: mess a lot with the clock: simulate a hang of up to 30 seconds
if i < desiredBlocks/2 { // only in first 50% of blocks to let it heal, hangs take time
delta = time.Duration(rng.Float64() * float64(time.Second*30))
}
} else if x < 0.1 { // 9%: mess with the timing, -50% to 50% off
delta = time.Duration((0.5 + rng.Float64()) * float64(delta))
} else if x < 0.5 {
// 40%: mess slightly with the timing, -10% to 10% off
delta = time.Duration((0.9 + rng.Float64()*0.2) * float64(delta))
}
clockTime = clockTime.Add(delta)
// reset errors
originErr = nil
attrsErr = nil
engControl.err = nil
engControl.errTyp = derive.BlockInsertOK
// maybe make something maybe fail, or try a new L1 origin
switch rng.Intn(10) { // 40% chance to fail sequencer action (!!!)
case 0:
originErr = errors.New("mock origin error")
case 1:
attrsErr = errors.New("mock attributes error")
case 2:
engControl.err = errors.New("mock temporary engine error")
engControl.errTyp = derive.BlockInsertTemporaryErr
case 3:
engControl.err = errors.New("mock prestate engine error")
engControl.errTyp = derive.BlockInsertPrestateErr
default:
// no error
}
payload := seq.RunNextSequencerAction(context.Background(), l1Head)
if payload != nil {
require.Equal(t, engControl.UnsafeL2Head().ID(), payload.ID(), "head must stay in sync with emitted payloads")
var tx types.Transaction
require.NoError(t, tx.UnmarshalBinary(payload.Transactions[0]))
info, err := derive.L1InfoDepositTxData(tx.Data())
require.NoError(t, err)
require.GreaterOrEqual(t, uint64(payload.Timestamp), info.Time, "ensure L2 time >= L1 time")
}
}
// Now, even though:
// - the start state was behind the wallclock
// - the L1 origin was far behind the L2
// - we made all components fail at random
// - messed with the clock
// the L2 chain was still built and stats are healthy on average!
l2Head := engControl.UnsafeL2Head()
t.Logf("avg build time: %s, clock timestamp: %d, L2 head time: %d, L1 origin time: %d, avg txs per block: %f", engControl.avgBuildingTime(), clockFn().Unix(), l2Head.Time, l1Times[l2Head.L1Origin], engControl.avgTxsPerBlock())
require.Equal(t, engControl.totalBuiltBlocks, desiredBlocks, "persist through random errors and build the desired blocks")
require.Equal(t, l2Head.Time, cfg.Genesis.L2Time+uint64(desiredBlocks)*cfg.BlockTime, "reached desired L2 block timestamp")
require.GreaterOrEqual(t, l2Head.Time, l1Times[l2Head.L1Origin], "the L2 time >= the L1 time")
require.Less(t, l2Head.Time-l1Times[l2Head.L1Origin], uint64(100), "The L1 origin time is close to the L2 time")
require.Less(t, clockTime.Sub(time.Unix(int64(l2Head.Time), 0)).Abs(), 2*time.Second, "L2 time is accurate, within 2 seconds of wallclock")
require.Greater(t, engControl.avgBuildingTime(), time.Second, "With 2 second block time and 1 second error backoff and healthy-on-average errors, building time should at least be a second")
require.Greater(t, engControl.avgTxsPerBlock(), 3.0, "We expect at least 1 system tx per block, but with a mocked 0-10 txs we expect an higher avg")
}
......@@ -25,8 +25,6 @@ type SyncStatus = eth.SyncStatus
// sealingDuration defines the expected time it takes to seal the block
const sealingDuration = time.Millisecond * 50
var UninitializedL1StateErr = errors.New("the L1 Head in L1 State is not initialized yet")
type Driver struct {
l1State L1StateIface
......@@ -73,7 +71,6 @@ type Driver struct {
l1 L1Chain
l2 L2Chain
l1OriginSelector L1OriginSelectorIface
sequencer SequencerIface
network Network // may be nil, network for is optional
......@@ -142,75 +139,6 @@ func (s *Driver) OnUnsafeL2Payload(ctx context.Context, payload *eth.ExecutionPa
}
}
// startNewL2Block starts sequencing a new L2 block on top of the unsafe L2 Head.
func (s *Driver) startNewL2Block(ctx context.Context) error {
l2Head := s.derivation.UnsafeL2Head()
l1Head := s.l1State.L1Head()
if l1Head == (eth.L1BlockRef{}) {
return UninitializedL1StateErr
}
// Figure out which L1 origin block we're going to be building on top of.
l1Origin, err := s.l1OriginSelector.FindL1Origin(ctx, l1Head, l2Head)
if err != nil {
s.log.Error("Error finding next L1 Origin", "err", err)
return err
}
// Rollup is configured to not start producing blocks until a specific L1 block has been
// reached. Don't produce any blocks until we're at that genesis block.
if l1Origin.Number < s.config.Genesis.L1.Number {
s.log.Info("Skipping block production because the next L1 Origin is behind the L1 genesis", "next", l1Origin.ID(), "genesis", s.config.Genesis.L1)
return fmt.Errorf("the L1 origin %s cannot be before genesis at %s", l1Origin, s.config.Genesis.L1)
}
// Should never happen. Sequencer will halt if we get into this situation somehow.
nextL2Time := l2Head.Time + s.config.BlockTime
if nextL2Time < l1Origin.Time {
s.log.Error("Cannot build L2 block for time before L1 origin",
"l2Unsafe", l2Head, "nextL2Time", nextL2Time, "l1Origin", l1Origin, "l1OriginTime", l1Origin.Time)
return fmt.Errorf("cannot build L2 block on top %s for time %d before L1 origin %s at time %d",
l2Head, nextL2Time, l1Origin, l1Origin.Time)
}
// Start creating the new block.
return s.sequencer.StartBuildingBlock(ctx, l1Origin)
}
// completeNewBlock completes a previously started L2 block sequencing job.
func (s *Driver) completeNewBlock(ctx context.Context) error {
payload, err := s.sequencer.CompleteBuildingBlock(ctx)
if err != nil {
s.metrics.RecordSequencingError()
s.log.Error("Failed to seal block as sequencer", "err", err)
return err
}
// Generate an L2 block ref from the payload.
newUnsafeL2Head, err := derive.PayloadToBlockRef(payload, &s.config.Genesis)
if err != nil {
s.metrics.RecordSequencingError()
s.log.Error("Sequenced payload cannot be transformed into valid L2 block reference", "err", err)
return fmt.Errorf("sequenced payload cannot be transformed into valid L2 block reference: %w", err)
}
// Update our L2 head block based on the new unsafe block we just generated.
s.derivation.SetUnsafeHead(newUnsafeL2Head)
s.log.Info("Sequenced new l2 block", "l2_unsafe", newUnsafeL2Head, "l1_origin", newUnsafeL2Head.L1Origin, "txs", len(payload.Transactions), "time", newUnsafeL2Head.Time)
s.metrics.CountSequencedTxs(len(payload.Transactions))
if s.network != nil {
if err := s.network.PublishL2Payload(ctx, payload); err != nil {
s.log.Warn("failed to publish newly created block", "id", payload.ID(), "err", err)
s.metrics.RecordPublishingError()
// publishing of unsafe data via p2p is optional. Errors are not severe enough to change/halt sequencing but should be logged and metered.
}
}
return nil
}
// the eventLoop responds to L1 changes and internal timers to produce L2 blocks.
func (s *Driver) eventLoop() {
defer s.wg.Done()
......@@ -259,34 +187,23 @@ func (s *Driver) eventLoop() {
// L1 chain that we need to handle.
reqStep()
blockTime := time.Duration(s.config.BlockTime) * time.Second
var sequenceErr error
var sequenceErrTime time.Time
sequencerTimer := time.NewTimer(0)
var sequencerCh <-chan time.Time
var sequencingPlannedOnto eth.BlockID
var sequencerSealNext bool
planSequencerAction := func() {
delay, seal, onto := s.sequencer.PlanNextSequencerAction(sequenceErr)
if sequenceErr != nil && time.Since(sequenceErrTime) > delay {
sequenceErr = nil
}
delay := s.sequencer.PlanNextSequencerAction()
sequencerCh = sequencerTimer.C
if len(sequencerCh) > 0 { // empty if not already drained before resetting
<-sequencerCh
}
sequencerTimer.Reset(delay)
sequencingPlannedOnto = onto
sequencerSealNext = seal
}
for {
// If we are sequencing, update the trigger for the next sequencer action.
// If we are sequencing, and the L1 state is ready, update the trigger for the next sequencer action.
// This may adjust at any time based on fork-choice changes or previous errors.
if s.driverConfig.SequencerEnabled && !s.driverConfig.SequencerStopped {
if s.driverConfig.SequencerEnabled && !s.driverConfig.SequencerStopped && s.l1State.L1Head() != (eth.L1BlockRef{}) {
// update sequencer time if the head changed
if sequencingPlannedOnto != s.derivation.UnsafeL2Head().ID() {
if s.sequencer.BuildingOnto().ID() != s.derivation.UnsafeL2Head().ID() {
planSequencerAction()
}
} else {
......@@ -295,22 +212,14 @@ func (s *Driver) eventLoop() {
select {
case <-sequencerCh:
s.log.Info("sequencing now!", "seal", sequencerSealNext, "idle_derivation", s.idleDerivation)
if sequencerSealNext {
// try to seal the current block task, and allow it to take up to 3 block times.
// If this fails we will simply start a new block building job.
ctx, cancel := context.WithTimeout(ctx, 3*blockTime)
sequenceErr = s.completeNewBlock(ctx)
cancel()
} else {
// Start the block building, don't allow the starting of sequencing to get stuck for more the time of 1 block.
ctx, cancel := context.WithTimeout(ctx, blockTime)
sequenceErr = s.startNewL2Block(ctx)
cancel()
payload := s.sequencer.RunNextSequencerAction(ctx, s.l1State.L1Head())
if s.network != nil && payload != nil {
// Publishing of unsafe data via p2p is optional.
// Errors are not severe enough to change/halt sequencing but should be logged and metered.
if err := s.network.PublishL2Payload(ctx, payload); err != nil {
s.log.Warn("failed to publish newly created block", "id", payload.ID(), "err", err)
s.metrics.RecordPublishingError()
}
if sequenceErr != nil {
s.log.Error("sequencing error", "err", sequenceErr)
sequenceErrTime = time.Now()
}
planSequencerAction() // schedule the next sequencer action to keep the sequencing looping
case payload := <-s.unsafeL2Payloads:
......@@ -386,8 +295,8 @@ func (s *Driver) eventLoop() {
} else {
s.log.Info("Sequencer has been started")
s.driverConfig.SequencerStopped = false
sequencingPlannedOnto = eth.BlockID{}
close(resp.err)
planSequencerAction() // resume sequencing
}
case respCh := <-s.stopSequencer:
if s.driverConfig.SequencerStopped {
......
// On develop
package driver
import (
"context"
"errors"
"math/big"
"math/rand"
"testing"
"time"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/log"
"github.com/stretchr/testify/require"
"github.com/ethereum-optimism/optimism/op-node/eth"
"github.com/ethereum-optimism/optimism/op-node/metrics"
"github.com/ethereum-optimism/optimism/op-node/rollup"
"github.com/ethereum-optimism/optimism/op-node/rollup/derive"
"github.com/ethereum-optimism/optimism/op-node/testutils"
)
type TestDummyOutputImpl struct {
willError bool
cfg *rollup.Config
l1Origin eth.L1BlockRef
l2Head eth.L2BlockRef
}
func (d *TestDummyOutputImpl) PlanNextSequencerAction(sequenceErr error) (delay time.Duration, seal bool, onto eth.BlockID) {
return 0, d.l1Origin != (eth.L1BlockRef{}), d.l2Head.ParentID()
}
func (d *TestDummyOutputImpl) StartBuildingBlock(ctx context.Context, l1Origin eth.L1BlockRef) error {
d.l1Origin = l1Origin
return nil
}
func (d *TestDummyOutputImpl) CompleteBuildingBlock(ctx context.Context) (*eth.ExecutionPayload, error) {
// If we're meant to error, return one
if d.willError {
return nil, errors.New("the TestDummyOutputImpl.createNewBlock operation failed")
}
info := &testutils.MockBlockInfo{
InfoHash: d.l1Origin.Hash,
InfoParentHash: d.l1Origin.ParentHash,
InfoCoinbase: common.Address{},
InfoRoot: common.Hash{},
InfoNum: d.l1Origin.Number,
InfoTime: d.l1Origin.Time,
InfoMixDigest: [32]byte{},
InfoBaseFee: big.NewInt(123),
InfoReceiptRoot: common.Hash{},
}
infoTx, err := derive.L1InfoDepositBytes(d.l2Head.SequenceNumber, info, eth.SystemConfig{})
if err != nil {
panic(err)
}
payload := eth.ExecutionPayload{
ParentHash: d.l2Head.Hash,
FeeRecipient: common.Address{},
StateRoot: eth.Bytes32{},
ReceiptsRoot: eth.Bytes32{},
LogsBloom: eth.Bytes256{},
PrevRandao: eth.Bytes32{},
BlockNumber: eth.Uint64Quantity(d.l2Head.Number + 1),
GasLimit: 0,
GasUsed: 0,
Timestamp: eth.Uint64Quantity(d.l2Head.Time + d.cfg.BlockTime),
ExtraData: nil,
BaseFeePerGas: eth.Uint256Quantity{},
BlockHash: common.Hash{123},
Transactions: []eth.Data{infoTx},
}
return &payload, nil
}
var _ SequencerIface = (*TestDummyOutputImpl)(nil)
type TestDummyDerivationPipeline struct {
DerivationPipeline
l2Head eth.L2BlockRef
l2SafeHead eth.L2BlockRef
l2Finalized eth.L2BlockRef
}
func (d TestDummyDerivationPipeline) Reset() {}
func (d TestDummyDerivationPipeline) Step(ctx context.Context) error { return nil }
func (d TestDummyDerivationPipeline) SetUnsafeHead(head eth.L2BlockRef) {}
func (d TestDummyDerivationPipeline) AddUnsafePayload(payload *eth.ExecutionPayload) {}
func (d TestDummyDerivationPipeline) Finalized() eth.L2BlockRef { return d.l2Head }
func (d TestDummyDerivationPipeline) SafeL2Head() eth.L2BlockRef { return d.l2SafeHead }
func (d TestDummyDerivationPipeline) UnsafeL2Head() eth.L2BlockRef { return d.l2Finalized }
type TestDummyL1OriginSelector struct {
retval eth.L1BlockRef
}
func (l TestDummyL1OriginSelector) FindL1Origin(ctx context.Context, l1Head eth.L1BlockRef, l2Head eth.L2BlockRef) (eth.L1BlockRef, error) {
return l.retval, nil
}
// TestRejectCreateBlockBadTimestamp tests that a block creation with invalid timestamps will be caught.
// This does not test:
// - The findL1Origin call (it is hardcoded to be the head)
// - The outputInterface used to create a new block from a given payload.
// - The DerivationPipeline setting unsafe head (a mock provider is used to pretend to set it)
// - Metrics (only mocked enough to let the method proceed)
// - Publishing (network is set to nil so publishing won't occur)
func TestRejectCreateBlockBadTimestamp(t *testing.T) {
// Create our random provider
rng := rand.New(rand.NewSource(rand.Int63()))
// Create our context for methods to execute under
ctx := context.Background()
// Create our fake L1/L2 heads and link them accordingly
l1HeadRef := testutils.RandomBlockRef(rng)
l2HeadRef := testutils.RandomL2BlockRef(rng)
l2l1OriginBlock := l1HeadRef
l2HeadRef.L1Origin = l2l1OriginBlock.ID()
// Create a rollup config
cfg := rollup.Config{
BlockTime: uint64(60),
Genesis: rollup.Genesis{
L1: l1HeadRef.ID(),
L2: l2HeadRef.ID(),
L2Time: 0x7000, // dummy value
},
}
// Patch our timestamp so we fail
l2HeadRef.Time = l2l1OriginBlock.Time - (cfg.BlockTime * 2)
// Create our outputter
outputProvider := &TestDummyOutputImpl{cfg: &cfg, l2Head: l2HeadRef, willError: false}
// Create our state
s := Driver{
l1State: &L1State{
l1Head: l1HeadRef,
log: log.New(),
metrics: metrics.NoopMetrics,
},
log: log.New(),
l1OriginSelector: TestDummyL1OriginSelector{retval: l1HeadRef},
config: &cfg,
sequencer: outputProvider,
derivation: TestDummyDerivationPipeline{},
metrics: metrics.NoopMetrics,
}
// Create a new block
// - L2Head's L1Origin, its timestamp should be greater than L1 genesis.
// - L2Head timestamp + BlockTime should be greater than or equal to the L1 Time.
err := s.startNewL2Block(ctx)
if err == nil {
err = s.completeNewBlock(ctx)
}
// Verify the L1Origin's block number is greater than L1 genesis in our config.
if l2l1OriginBlock.Number < s.config.Genesis.L1.Number {
require.NoError(t, err, "L1Origin block number should be greater than the L1 genesis block number")
}
// Verify the new L2 block to create will have a time stamp equal or newer than our L1 origin block we derive from.
if l2HeadRef.Time+cfg.BlockTime < l2l1OriginBlock.Time {
// If not, we expect a specific error.
// TODO: This isn't the cleanest, we should construct + compare the whole error message.
require.NotNil(t, err)
require.Contains(t, err.Error(), "cannot build L2 block on top")
require.Contains(t, err.Error(), "for time")
require.Contains(t, err.Error(), "before L1 origin")
return
}
// If we expected the outputter to error, capture that here
if outputProvider.willError {
require.NotNil(t, err, "outputInterface failed to createNewBlock, so createNewL2Block should also have failed")
return
}
// Otherwise we should have no error.
require.NoError(t, err, "error raised in TestRejectCreateBlockBadTimestamp")
}
// FuzzRejectCreateBlockBadTimestamp is a property test derived from the TestRejectCreateBlockBadTimestamp unit test.
// It fuzzes timestamps and block times to find a configuration to violate error checking.
func FuzzRejectCreateBlockBadTimestamp(f *testing.F) {
f.Fuzz(func(t *testing.T, randSeed int64, l2Time uint64, blockTime uint64, forceOutputFail bool, currentL2HeadTime uint64) {
// Create our random provider
rng := rand.New(rand.NewSource(randSeed))
// Create our context for methods to execute under
ctx := context.Background()
// Create our fake L1/L2 heads and link them accordingly
l1HeadRef := testutils.RandomBlockRef(rng)
l2HeadRef := testutils.RandomL2BlockRef(rng)
l2l1OriginBlock := l1HeadRef
l2HeadRef.L1Origin = l2l1OriginBlock.ID()
// TODO: Cap our block time so it doesn't overflow
if blockTime > 0x100000 {
blockTime = 0x100000
}
// Create a rollup config
cfg := rollup.Config{
BlockTime: blockTime,
Genesis: rollup.Genesis{
L1: l1HeadRef.ID(),
L2: l2HeadRef.ID(),
L2Time: l2Time, // dummy value
},
}
// Patch our timestamp so we fail
l2HeadRef.Time = currentL2HeadTime
// Create our outputter
outputProvider := &TestDummyOutputImpl{cfg: &cfg, l2Head: l2HeadRef, willError: forceOutputFail}
// Create our state
s := Driver{
l1State: &L1State{
l1Head: l1HeadRef,
log: log.New(),
metrics: metrics.NoopMetrics,
},
log: log.New(),
l1OriginSelector: TestDummyL1OriginSelector{retval: l1HeadRef},
config: &cfg,
sequencer: outputProvider,
derivation: TestDummyDerivationPipeline{},
metrics: metrics.NoopMetrics,
}
// Create a new block
// - L2Head's L1Origin, its timestamp should be greater than L1 genesis.
// - L2Head timestamp + BlockTime should be greater than or equal to the L1 Time.
err := s.startNewL2Block(ctx)
if err == nil {
err = s.completeNewBlock(ctx)
}
// Verify the L1Origin's timestamp is greater than L1 genesis in our config.
if l2l1OriginBlock.Number < s.config.Genesis.L1.Number {
require.NoError(t, err)
return
}
// Verify the new L2 block to create will have a time stamp equal or newer than our L1 origin block we derive from.
if l2HeadRef.Time+cfg.BlockTime < l2l1OriginBlock.Time {
// If not, we expect a specific error.
// TODO: This isn't the cleanest, we should construct + compare the whole error message.
require.NotNil(t, err)
require.Contains(t, err.Error(), "cannot build L2 block on top")
require.Contains(t, err.Error(), "for time")
require.Contains(t, err.Error(), "before L1 origin")
return
}
// Otherwise we should have no error.
require.Nil(t, err)
// If we expected the outputter to error, capture that here
if outputProvider.willError {
require.NotNil(t, err, "outputInterface failed to createNewBlock, so createNewL2Block should also have failed")
return
}
// Otherwise we should have no error.
require.NoError(t, err, "L1Origin block number should be greater than the L1 genesis block number")
})
}
{
"address": "0x1BEb19F1685ddF2F774884902119Fa2FA5d8f509",
"address": "0xbeD744818e96AAD8a51324291a6f6Cb20A0c22be",
"abi": [
{
"inputs": [],
......@@ -67,6 +67,29 @@
"stateMutability": "nonpayable",
"type": "function"
},
{
"inputs": [
{
"internalType": "address",
"name": "_about",
"type": "address"
},
{
"internalType": "bytes32",
"name": "_key",
"type": "bytes32"
},
{
"internalType": "bytes",
"name": "_val",
"type": "bytes"
}
],
"name": "attest",
"outputs": [],
"stateMutability": "nonpayable",
"type": "function"
},
{
"inputs": [
{
......@@ -110,28 +133,28 @@
"type": "function"
}
],
"transactionHash": "0xfea89fc1eefeaa83bd8d88eb9002594746c12b6761d674f82e821567483df315",
"transactionHash": "0x4c0aae61c0030872cb5f8654c36fbc192cfab6f09f93df5c0f80268382d964e9",
"receipt": {
"to": "0x4e59b44847b379578588920cA78FbF26c0B4956C",
"from": "0x9C6373dE60c2D3297b18A8f964618ac46E011B58",
"from": "0x956a5152D0f498dBA0c5966577bb44262F8F7078",
"contractAddress": null,
"transactionIndex": 0,
"gasUsed": "645110",
"transactionIndex": 1,
"gasUsed": "666309",
"logsBloom": "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
"blockHash": "0x6043e32ed00ca470e6676a00f619eb1fed995b06b0159f77ff545b950bae515a",
"transactionHash": "0xfea89fc1eefeaa83bd8d88eb9002594746c12b6761d674f82e821567483df315",
"blockHash": "0xe683044625d767223da2ecd65fd81a34251ffbef27abd3302530be36e99971d0",
"transactionHash": "0x4c0aae61c0030872cb5f8654c36fbc192cfab6f09f93df5c0f80268382d964e9",
"logs": [],
"blockNumber": 3451191,
"cumulativeGasUsed": "645110",
"blockNumber": 4667191,
"cumulativeGasUsed": "666309",
"status": 1,
"byzantium": true
},
"args": [],
"numDeployments": 1,
"solcInputHash": "45837d34ff24b9cb2ae34232b60ea874",
"metadata": "{\"compiler\":{\"version\":\"0.8.15+commit.e14f2714\"},\"language\":\"Solidity\",\"output\":{\"abi\":[{\"inputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"constructor\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"address\",\"name\":\"creator\",\"type\":\"address\"},{\"indexed\":true,\"internalType\":\"address\",\"name\":\"about\",\"type\":\"address\"},{\"indexed\":true,\"internalType\":\"bytes32\",\"name\":\"key\",\"type\":\"bytes32\"},{\"indexed\":false,\"internalType\":\"bytes\",\"name\":\"val\",\"type\":\"bytes\"}],\"name\":\"AttestationCreated\",\"type\":\"event\"},{\"inputs\":[{\"components\":[{\"internalType\":\"address\",\"name\":\"about\",\"type\":\"address\"},{\"internalType\":\"bytes32\",\"name\":\"key\",\"type\":\"bytes32\"},{\"internalType\":\"bytes\",\"name\":\"val\",\"type\":\"bytes\"}],\"internalType\":\"struct AttestationStation.AttestationData[]\",\"name\":\"_attestations\",\"type\":\"tuple[]\"}],\"name\":\"attest\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"},{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"},{\"internalType\":\"bytes32\",\"name\":\"\",\"type\":\"bytes32\"}],\"name\":\"attestations\",\"outputs\":[{\"internalType\":\"bytes\",\"name\":\"\",\"type\":\"bytes\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"version\",\"outputs\":[{\"internalType\":\"string\",\"name\":\"\",\"type\":\"string\"}],\"stateMutability\":\"view\",\"type\":\"function\"}],\"devdoc\":{\"author\":\"Optimism CollectiveGitcoin\",\"events\":{\"AttestationCreated(address,address,bytes32,bytes)\":{\"params\":{\"about\":\"Address attestation is about.\",\"creator\":\"Address that made the attestation.\",\"key\":\"Key of the attestation.\",\"val\":\"Value of the attestation.\"}}},\"kind\":\"dev\",\"methods\":{\"attest((address,bytes32,bytes)[])\":{\"params\":{\"_attestations\":\"An array of attestation data.\"}},\"constructor\":{\"custom:semver\":\"1.0.0\"},\"version()\":{\"returns\":{\"_0\":\"Semver contract version as a string.\"}}},\"title\":\"AttestationStation\",\"version\":1},\"userdoc\":{\"events\":{\"AttestationCreated(address,address,bytes32,bytes)\":{\"notice\":\"Emitted when Attestation is created.\"}},\"kind\":\"user\",\"methods\":{\"attest((address,bytes32,bytes)[])\":{\"notice\":\"Allows anyone to create attestations.\"},\"attestations(address,address,bytes32)\":{\"notice\":\"Maps addresses to attestations. Creator => About => Key => Value.\"},\"version()\":{\"notice\":\"Returns the full semver contract version.\"}},\"notice\":\"Where attestations live.\",\"version\":1}},\"settings\":{\"compilationTarget\":{\"contracts/universal/op-nft/AttestationStation.sol\":\"AttestationStation\"},\"evmVersion\":\"london\",\"libraries\":{},\"metadata\":{\"bytecodeHash\":\"ipfs\",\"useLiteralContent\":true},\"optimizer\":{\"enabled\":true,\"runs\":10000},\"remappings\":[]},\"sources\":{\"@eth-optimism/contracts-bedrock/contracts/universal/Semver.sol\":{\"content\":\"// SPDX-License-Identifier: MIT\\npragma solidity ^0.8.15;\\n\\nimport { Strings } from \\\"@openzeppelin/contracts/utils/Strings.sol\\\";\\n\\n/**\\n * @title Semver\\n * @notice Semver is a simple contract for managing contract versions.\\n */\\ncontract Semver {\\n /**\\n * @notice Contract version number (major).\\n */\\n uint256 private immutable MAJOR_VERSION;\\n\\n /**\\n * @notice Contract version number (minor).\\n */\\n uint256 private immutable MINOR_VERSION;\\n\\n /**\\n * @notice Contract version number (patch).\\n */\\n uint256 private immutable PATCH_VERSION;\\n\\n /**\\n * @param _major Version number (major).\\n * @param _minor Version number (minor).\\n * @param _patch Version number (patch).\\n */\\n constructor(\\n uint256 _major,\\n uint256 _minor,\\n uint256 _patch\\n ) {\\n MAJOR_VERSION = _major;\\n MINOR_VERSION = _minor;\\n PATCH_VERSION = _patch;\\n }\\n\\n /**\\n * @notice Returns the full semver contract version.\\n *\\n * @return Semver contract version as a string.\\n */\\n function version() public view returns (string memory) {\\n return\\n string(\\n abi.encodePacked(\\n Strings.toString(MAJOR_VERSION),\\n \\\".\\\",\\n Strings.toString(MINOR_VERSION),\\n \\\".\\\",\\n Strings.toString(PATCH_VERSION)\\n )\\n );\\n }\\n}\\n\",\"keccak256\":\"0x979b13465de4996a1105850abbf48abe7f71d5e18a8d4af318597ee14c165fdf\",\"license\":\"MIT\"},\"@openzeppelin/contracts/utils/Strings.sol\":{\"content\":\"// SPDX-License-Identifier: MIT\\n// OpenZeppelin Contracts (last updated v4.7.0) (utils/Strings.sol)\\n\\npragma solidity ^0.8.0;\\n\\n/**\\n * @dev String operations.\\n */\\nlibrary Strings {\\n bytes16 private constant _HEX_SYMBOLS = \\\"0123456789abcdef\\\";\\n uint8 private constant _ADDRESS_LENGTH = 20;\\n\\n /**\\n * @dev Converts a `uint256` to its ASCII `string` decimal representation.\\n */\\n function toString(uint256 value) internal pure returns (string memory) {\\n // Inspired by OraclizeAPI's implementation - MIT licence\\n // https://github.com/oraclize/ethereum-api/blob/b42146b063c7d6ee1358846c198246239e9360e8/oraclizeAPI_0.4.25.sol\\n\\n if (value == 0) {\\n return \\\"0\\\";\\n }\\n uint256 temp = value;\\n uint256 digits;\\n while (temp != 0) {\\n digits++;\\n temp /= 10;\\n }\\n bytes memory buffer = new bytes(digits);\\n while (value != 0) {\\n digits -= 1;\\n buffer[digits] = bytes1(uint8(48 + uint256(value % 10)));\\n value /= 10;\\n }\\n return string(buffer);\\n }\\n\\n /**\\n * @dev Converts a `uint256` to its ASCII `string` hexadecimal representation.\\n */\\n function toHexString(uint256 value) internal pure returns (string memory) {\\n if (value == 0) {\\n return \\\"0x00\\\";\\n }\\n uint256 temp = value;\\n uint256 length = 0;\\n while (temp != 0) {\\n length++;\\n temp >>= 8;\\n }\\n return toHexString(value, length);\\n }\\n\\n /**\\n * @dev Converts a `uint256` to its ASCII `string` hexadecimal representation with fixed length.\\n */\\n function toHexString(uint256 value, uint256 length) internal pure returns (string memory) {\\n bytes memory buffer = new bytes(2 * length + 2);\\n buffer[0] = \\\"0\\\";\\n buffer[1] = \\\"x\\\";\\n for (uint256 i = 2 * length + 1; i > 1; --i) {\\n buffer[i] = _HEX_SYMBOLS[value & 0xf];\\n value >>= 4;\\n }\\n require(value == 0, \\\"Strings: hex length insufficient\\\");\\n return string(buffer);\\n }\\n\\n /**\\n * @dev Converts an `address` with fixed length of 20 bytes to its not checksummed ASCII `string` hexadecimal representation.\\n */\\n function toHexString(address addr) internal pure returns (string memory) {\\n return toHexString(uint256(uint160(addr)), _ADDRESS_LENGTH);\\n }\\n}\\n\",\"keccak256\":\"0xaf159a8b1923ad2a26d516089bceca9bdeaeacd04be50983ea00ba63070f08a3\",\"license\":\"MIT\"},\"contracts/universal/op-nft/AttestationStation.sol\":{\"content\":\"// SPDX-License-Identifier: MIT\\npragma solidity 0.8.15;\\n\\nimport { Semver } from \\\"@eth-optimism/contracts-bedrock/contracts/universal/Semver.sol\\\";\\n\\n/**\\n * @title AttestationStation\\n * @author Optimism Collective\\n * @author Gitcoin\\n * @notice Where attestations live.\\n */\\ncontract AttestationStation is Semver {\\n /**\\n * @notice Struct representing data that is being attested.\\n *\\n * @custom:field about Address for which the attestation is about.\\n * @custom:field key A bytes32 key for the attestation.\\n * @custom:field val The attestation as arbitrary bytes.\\n */\\n struct AttestationData {\\n address about;\\n bytes32 key;\\n bytes val;\\n }\\n\\n /**\\n * @notice Maps addresses to attestations. Creator => About => Key => Value.\\n */\\n mapping(address => mapping(address => mapping(bytes32 => bytes))) public attestations;\\n\\n /**\\n * @notice Emitted when Attestation is created.\\n *\\n * @param creator Address that made the attestation.\\n * @param about Address attestation is about.\\n * @param key Key of the attestation.\\n * @param val Value of the attestation.\\n */\\n event AttestationCreated(\\n address indexed creator,\\n address indexed about,\\n bytes32 indexed key,\\n bytes val\\n );\\n\\n /**\\n * @custom:semver 1.0.0\\n */\\n constructor() Semver(1, 0, 0) {}\\n\\n /**\\n * @notice Allows anyone to create attestations.\\n *\\n * @param _attestations An array of attestation data.\\n */\\n function attest(AttestationData[] memory _attestations) public {\\n uint256 length = _attestations.length;\\n for (uint256 i = 0; i < length; ) {\\n AttestationData memory attestation = _attestations[i];\\n attestations[msg.sender][attestation.about][attestation.key] = attestation.val;\\n\\n emit AttestationCreated(\\n msg.sender,\\n attestation.about,\\n attestation.key,\\n attestation.val\\n );\\n\\n unchecked {\\n ++i;\\n }\\n }\\n }\\n}\\n\",\"keccak256\":\"0x670fde721c291828c3f6343a40bb315dbbcc5e4411125158892ffe54459d69b7\",\"license\":\"MIT\"}},\"version\":1}",
"bytecode": "0x60e060405234801561001057600080fd5b5060016080819052600060a081905260c081905280610aba61004a8239600061018f015260006101660152600061013d0152610aba6000f3fe608060405234801561001057600080fd5b50600436106100415760003560e01c806329b42cb51461004657806354fd4d501461006f5780635eb5ea1014610077575b600080fd5b610059610054366004610437565b61008c565b60405161006691906104ed565b60405180910390f35b610059610136565b61008a6100853660046105ae565b6101d9565b005b60006020818152938152604080822085529281528281209093528252902080546100b590610737565b80601f01602080910402602001604051908101604052809291908181526020018280546100e190610737565b801561012e5780601f106101035761010080835404028352916020019161012e565b820191906000526020600020905b81548152906001019060200180831161011157829003601f168201915b505050505081565b60606101617f00000000000000000000000000000000000000000000000000000000000000006102d1565b61018a7f00000000000000000000000000000000000000000000000000000000000000006102d1565b6101b37f00000000000000000000000000000000000000000000000000000000000000006102d1565b6040516020016101c59392919061078a565b604051602081830303815290604052905090565b805160005b818110156102cc5760008382815181106101fa576101fa610800565b602090810291909101810151604080820151336000908152808552828120845173ffffffffffffffffffffffffffffffffffffffff1682528552828120848601518252909452922090925090610250908261087d565b508060200151816000015173ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff167f28710dfecab43d1e29e02aa56b2e1e610c0bae19135c9cf7a83a1adb6df96d8584604001516040516102bb91906104ed565b60405180910390a4506001016101de565b505050565b60608160000361031457505060408051808201909152600181527f3000000000000000000000000000000000000000000000000000000000000000602082015290565b8160005b811561033e5780610328816109c6565b91506103379050600a83610a2d565b9150610318565b60008167ffffffffffffffff81111561035957610359610507565b6040519080825280601f01601f191660200182016040528015610383576020820181803683370190505b5090505b841561040657610398600183610a41565b91506103a5600a86610a58565b6103b0906030610a6c565b60f81b8183815181106103c5576103c5610800565b60200101907effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff1916908160001a9053506103ff600a86610a2d565b9450610387565b949350505050565b803573ffffffffffffffffffffffffffffffffffffffff8116811461043257600080fd5b919050565b60008060006060848603121561044c57600080fd5b6104558461040e565b92506104636020850161040e565b9150604084013590509250925092565b60005b8381101561048e578181015183820152602001610476565b8381111561049d576000848401525b50505050565b600081518084526104bb816020860160208601610473565b601f017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0169290920160200192915050565b60208152600061050060208301846104a3565b9392505050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b6040516060810167ffffffffffffffff8111828210171561055957610559610507565b60405290565b604051601f82017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe016810167ffffffffffffffff811182821017156105a6576105a6610507565b604052919050565b600060208083850312156105c157600080fd5b823567ffffffffffffffff808211156105d957600080fd5b818501915085601f8301126105ed57600080fd5b8135818111156105ff576105ff610507565b8060051b61060e85820161055f565b918252838101850191858101908984111561062857600080fd5b86860192505b8383101561072a578235858111156106465760008081fd5b860160607fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0828d03810182131561067d5760008081fd5b610685610536565b6106908b850161040e565b81526040848101358c8301529284013592898411156106af5760008081fd5b83850194508e603f8601126106c657600093508384fd5b8b8501359350898411156106dc576106dc610507565b6106ec8c84601f8701160161055f565b92508383528e818587010111156107035760008081fd5b838186018d85013760009383018c019390935291820152835250918601919086019061062e565b9998505050505050505050565b600181811c9082168061074b57607f821691505b602082108103610784577f4e487b7100000000000000000000000000000000000000000000000000000000600052602260045260246000fd5b50919050565b6000845161079c818460208901610473565b80830190507f2e0000000000000000000000000000000000000000000000000000000000000080825285516107d8816001850160208a01610473565b600192019182015283516107f3816002840160208801610473565b0160020195945050505050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052603260045260246000fd5b601f8211156102cc57600081815260208120601f850160051c810160208610156108565750805b601f850160051c820191505b8181101561087557828155600101610862565b505050505050565b815167ffffffffffffffff81111561089757610897610507565b6108ab816108a58454610737565b8461082f565b602080601f8311600181146108fe57600084156108c85750858301515b7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff600386901b1c1916600185901b178555610875565b6000858152602081207fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe08616915b8281101561094b5788860151825594840194600190910190840161092c565b508582101561098757878501517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff600388901b60f8161c191681555b5050505050600190811b01905550565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601160045260246000fd5b60007fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff82036109f7576109f7610997565b5060010190565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601260045260246000fd5b600082610a3c57610a3c6109fe565b500490565b600082821015610a5357610a53610997565b500390565b600082610a6757610a676109fe565b500690565b60008219821115610a7f57610a7f610997565b50019056fea26469706673582212204abcd56633387050a41a0ae027bbf9077ae2fb5080fa84124e2aa381adb3f98964736f6c634300080f0033",
"deployedBytecode": "0x608060405234801561001057600080fd5b50600436106100415760003560e01c806329b42cb51461004657806354fd4d501461006f5780635eb5ea1014610077575b600080fd5b610059610054366004610437565b61008c565b60405161006691906104ed565b60405180910390f35b610059610136565b61008a6100853660046105ae565b6101d9565b005b60006020818152938152604080822085529281528281209093528252902080546100b590610737565b80601f01602080910402602001604051908101604052809291908181526020018280546100e190610737565b801561012e5780601f106101035761010080835404028352916020019161012e565b820191906000526020600020905b81548152906001019060200180831161011157829003601f168201915b505050505081565b60606101617f00000000000000000000000000000000000000000000000000000000000000006102d1565b61018a7f00000000000000000000000000000000000000000000000000000000000000006102d1565b6101b37f00000000000000000000000000000000000000000000000000000000000000006102d1565b6040516020016101c59392919061078a565b604051602081830303815290604052905090565b805160005b818110156102cc5760008382815181106101fa576101fa610800565b602090810291909101810151604080820151336000908152808552828120845173ffffffffffffffffffffffffffffffffffffffff1682528552828120848601518252909452922090925090610250908261087d565b508060200151816000015173ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff167f28710dfecab43d1e29e02aa56b2e1e610c0bae19135c9cf7a83a1adb6df96d8584604001516040516102bb91906104ed565b60405180910390a4506001016101de565b505050565b60608160000361031457505060408051808201909152600181527f3000000000000000000000000000000000000000000000000000000000000000602082015290565b8160005b811561033e5780610328816109c6565b91506103379050600a83610a2d565b9150610318565b60008167ffffffffffffffff81111561035957610359610507565b6040519080825280601f01601f191660200182016040528015610383576020820181803683370190505b5090505b841561040657610398600183610a41565b91506103a5600a86610a58565b6103b0906030610a6c565b60f81b8183815181106103c5576103c5610800565b60200101907effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff1916908160001a9053506103ff600a86610a2d565b9450610387565b949350505050565b803573ffffffffffffffffffffffffffffffffffffffff8116811461043257600080fd5b919050565b60008060006060848603121561044c57600080fd5b6104558461040e565b92506104636020850161040e565b9150604084013590509250925092565b60005b8381101561048e578181015183820152602001610476565b8381111561049d576000848401525b50505050565b600081518084526104bb816020860160208601610473565b601f017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0169290920160200192915050565b60208152600061050060208301846104a3565b9392505050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b6040516060810167ffffffffffffffff8111828210171561055957610559610507565b60405290565b604051601f82017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe016810167ffffffffffffffff811182821017156105a6576105a6610507565b604052919050565b600060208083850312156105c157600080fd5b823567ffffffffffffffff808211156105d957600080fd5b818501915085601f8301126105ed57600080fd5b8135818111156105ff576105ff610507565b8060051b61060e85820161055f565b918252838101850191858101908984111561062857600080fd5b86860192505b8383101561072a578235858111156106465760008081fd5b860160607fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0828d03810182131561067d5760008081fd5b610685610536565b6106908b850161040e565b81526040848101358c8301529284013592898411156106af5760008081fd5b83850194508e603f8601126106c657600093508384fd5b8b8501359350898411156106dc576106dc610507565b6106ec8c84601f8701160161055f565b92508383528e818587010111156107035760008081fd5b838186018d85013760009383018c019390935291820152835250918601919086019061062e565b9998505050505050505050565b600181811c9082168061074b57607f821691505b602082108103610784577f4e487b7100000000000000000000000000000000000000000000000000000000600052602260045260246000fd5b50919050565b6000845161079c818460208901610473565b80830190507f2e0000000000000000000000000000000000000000000000000000000000000080825285516107d8816001850160208a01610473565b600192019182015283516107f3816002840160208801610473565b0160020195945050505050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052603260045260246000fd5b601f8211156102cc57600081815260208120601f850160051c810160208610156108565750805b601f850160051c820191505b8181101561087557828155600101610862565b505050505050565b815167ffffffffffffffff81111561089757610897610507565b6108ab816108a58454610737565b8461082f565b602080601f8311600181146108fe57600084156108c85750858301515b7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff600386901b1c1916600185901b178555610875565b6000858152602081207fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe08616915b8281101561094b5788860151825594840194600190910190840161092c565b508582101561098757878501517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff600388901b60f8161c191681555b5050505050600190811b01905550565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601160045260246000fd5b60007fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff82036109f7576109f7610997565b5060010190565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601260045260246000fd5b600082610a3c57610a3c6109fe565b500490565b600082821015610a5357610a53610997565b500390565b600082610a6757610a676109fe565b500690565b60008219821115610a7f57610a7f610997565b50019056fea26469706673582212204abcd56633387050a41a0ae027bbf9077ae2fb5080fa84124e2aa381adb3f98964736f6c634300080f0033",
"numDeployments": 2,
"solcInputHash": "3aa2ad7d005d9515a1f12df8da17d178",
"metadata": "{\"compiler\":{\"version\":\"0.8.15+commit.e14f2714\"},\"language\":\"Solidity\",\"output\":{\"abi\":[{\"inputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"constructor\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"address\",\"name\":\"creator\",\"type\":\"address\"},{\"indexed\":true,\"internalType\":\"address\",\"name\":\"about\",\"type\":\"address\"},{\"indexed\":true,\"internalType\":\"bytes32\",\"name\":\"key\",\"type\":\"bytes32\"},{\"indexed\":false,\"internalType\":\"bytes\",\"name\":\"val\",\"type\":\"bytes\"}],\"name\":\"AttestationCreated\",\"type\":\"event\"},{\"inputs\":[{\"components\":[{\"internalType\":\"address\",\"name\":\"about\",\"type\":\"address\"},{\"internalType\":\"bytes32\",\"name\":\"key\",\"type\":\"bytes32\"},{\"internalType\":\"bytes\",\"name\":\"val\",\"type\":\"bytes\"}],\"internalType\":\"struct AttestationStation.AttestationData[]\",\"name\":\"_attestations\",\"type\":\"tuple[]\"}],\"name\":\"attest\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"_about\",\"type\":\"address\"},{\"internalType\":\"bytes32\",\"name\":\"_key\",\"type\":\"bytes32\"},{\"internalType\":\"bytes\",\"name\":\"_val\",\"type\":\"bytes\"}],\"name\":\"attest\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"},{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"},{\"internalType\":\"bytes32\",\"name\":\"\",\"type\":\"bytes32\"}],\"name\":\"attestations\",\"outputs\":[{\"internalType\":\"bytes\",\"name\":\"\",\"type\":\"bytes\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"version\",\"outputs\":[{\"internalType\":\"string\",\"name\":\"\",\"type\":\"string\"}],\"stateMutability\":\"view\",\"type\":\"function\"}],\"devdoc\":{\"author\":\"Optimism CollectiveGitcoin\",\"events\":{\"AttestationCreated(address,address,bytes32,bytes)\":{\"params\":{\"about\":\"Address attestation is about.\",\"creator\":\"Address that made the attestation.\",\"key\":\"Key of the attestation.\",\"val\":\"Value of the attestation.\"}}},\"kind\":\"dev\",\"methods\":{\"attest((address,bytes32,bytes)[])\":{\"params\":{\"_attestations\":\"An array of attestation data.\"}},\"attest(address,bytes32,bytes)\":{\"params\":{\"_about\":\"Address that the attestation is about.\",\"_key\":\"A key used to namespace the attestation.\",\"_val\":\"An arbitrary value stored as part of the attestation.\"}},\"constructor\":{\"custom:semver\":\"1.1.0\"},\"version()\":{\"returns\":{\"_0\":\"Semver contract version as a string.\"}}},\"title\":\"AttestationStation\",\"version\":1},\"userdoc\":{\"events\":{\"AttestationCreated(address,address,bytes32,bytes)\":{\"notice\":\"Emitted when Attestation is created.\"}},\"kind\":\"user\",\"methods\":{\"attest((address,bytes32,bytes)[])\":{\"notice\":\"Allows anyone to create attestations.\"},\"attest(address,bytes32,bytes)\":{\"notice\":\"Allows anyone to create an attestation.\"},\"attestations(address,address,bytes32)\":{\"notice\":\"Maps addresses to attestations. Creator => About => Key => Value.\"},\"version()\":{\"notice\":\"Returns the full semver contract version.\"}},\"notice\":\"Where attestations live.\",\"version\":1}},\"settings\":{\"compilationTarget\":{\"contracts/universal/op-nft/AttestationStation.sol\":\"AttestationStation\"},\"evmVersion\":\"london\",\"libraries\":{},\"metadata\":{\"bytecodeHash\":\"ipfs\",\"useLiteralContent\":true},\"optimizer\":{\"enabled\":true,\"runs\":10000},\"remappings\":[]},\"sources\":{\"@eth-optimism/contracts-bedrock/contracts/universal/Semver.sol\":{\"content\":\"// SPDX-License-Identifier: MIT\\npragma solidity ^0.8.15;\\n\\nimport { Strings } from \\\"@openzeppelin/contracts/utils/Strings.sol\\\";\\n\\n/**\\n * @title Semver\\n * @notice Semver is a simple contract for managing contract versions.\\n */\\ncontract Semver {\\n /**\\n * @notice Contract version number (major).\\n */\\n uint256 private immutable MAJOR_VERSION;\\n\\n /**\\n * @notice Contract version number (minor).\\n */\\n uint256 private immutable MINOR_VERSION;\\n\\n /**\\n * @notice Contract version number (patch).\\n */\\n uint256 private immutable PATCH_VERSION;\\n\\n /**\\n * @param _major Version number (major).\\n * @param _minor Version number (minor).\\n * @param _patch Version number (patch).\\n */\\n constructor(\\n uint256 _major,\\n uint256 _minor,\\n uint256 _patch\\n ) {\\n MAJOR_VERSION = _major;\\n MINOR_VERSION = _minor;\\n PATCH_VERSION = _patch;\\n }\\n\\n /**\\n * @notice Returns the full semver contract version.\\n *\\n * @return Semver contract version as a string.\\n */\\n function version() public view returns (string memory) {\\n return\\n string(\\n abi.encodePacked(\\n Strings.toString(MAJOR_VERSION),\\n \\\".\\\",\\n Strings.toString(MINOR_VERSION),\\n \\\".\\\",\\n Strings.toString(PATCH_VERSION)\\n )\\n );\\n }\\n}\\n\",\"keccak256\":\"0x979b13465de4996a1105850abbf48abe7f71d5e18a8d4af318597ee14c165fdf\",\"license\":\"MIT\"},\"@openzeppelin/contracts/utils/Strings.sol\":{\"content\":\"// SPDX-License-Identifier: MIT\\n// OpenZeppelin Contracts (last updated v4.7.0) (utils/Strings.sol)\\n\\npragma solidity ^0.8.0;\\n\\n/**\\n * @dev String operations.\\n */\\nlibrary Strings {\\n bytes16 private constant _HEX_SYMBOLS = \\\"0123456789abcdef\\\";\\n uint8 private constant _ADDRESS_LENGTH = 20;\\n\\n /**\\n * @dev Converts a `uint256` to its ASCII `string` decimal representation.\\n */\\n function toString(uint256 value) internal pure returns (string memory) {\\n // Inspired by OraclizeAPI's implementation - MIT licence\\n // https://github.com/oraclize/ethereum-api/blob/b42146b063c7d6ee1358846c198246239e9360e8/oraclizeAPI_0.4.25.sol\\n\\n if (value == 0) {\\n return \\\"0\\\";\\n }\\n uint256 temp = value;\\n uint256 digits;\\n while (temp != 0) {\\n digits++;\\n temp /= 10;\\n }\\n bytes memory buffer = new bytes(digits);\\n while (value != 0) {\\n digits -= 1;\\n buffer[digits] = bytes1(uint8(48 + uint256(value % 10)));\\n value /= 10;\\n }\\n return string(buffer);\\n }\\n\\n /**\\n * @dev Converts a `uint256` to its ASCII `string` hexadecimal representation.\\n */\\n function toHexString(uint256 value) internal pure returns (string memory) {\\n if (value == 0) {\\n return \\\"0x00\\\";\\n }\\n uint256 temp = value;\\n uint256 length = 0;\\n while (temp != 0) {\\n length++;\\n temp >>= 8;\\n }\\n return toHexString(value, length);\\n }\\n\\n /**\\n * @dev Converts a `uint256` to its ASCII `string` hexadecimal representation with fixed length.\\n */\\n function toHexString(uint256 value, uint256 length) internal pure returns (string memory) {\\n bytes memory buffer = new bytes(2 * length + 2);\\n buffer[0] = \\\"0\\\";\\n buffer[1] = \\\"x\\\";\\n for (uint256 i = 2 * length + 1; i > 1; --i) {\\n buffer[i] = _HEX_SYMBOLS[value & 0xf];\\n value >>= 4;\\n }\\n require(value == 0, \\\"Strings: hex length insufficient\\\");\\n return string(buffer);\\n }\\n\\n /**\\n * @dev Converts an `address` with fixed length of 20 bytes to its not checksummed ASCII `string` hexadecimal representation.\\n */\\n function toHexString(address addr) internal pure returns (string memory) {\\n return toHexString(uint256(uint160(addr)), _ADDRESS_LENGTH);\\n }\\n}\\n\",\"keccak256\":\"0xaf159a8b1923ad2a26d516089bceca9bdeaeacd04be50983ea00ba63070f08a3\",\"license\":\"MIT\"},\"contracts/universal/op-nft/AttestationStation.sol\":{\"content\":\"// SPDX-License-Identifier: MIT\\npragma solidity 0.8.15;\\n\\nimport { Semver } from \\\"@eth-optimism/contracts-bedrock/contracts/universal/Semver.sol\\\";\\n\\n/**\\n * @title AttestationStation\\n * @author Optimism Collective\\n * @author Gitcoin\\n * @notice Where attestations live.\\n */\\ncontract AttestationStation is Semver {\\n /**\\n * @notice Struct representing data that is being attested.\\n *\\n * @custom:field about Address for which the attestation is about.\\n * @custom:field key A bytes32 key for the attestation.\\n * @custom:field val The attestation as arbitrary bytes.\\n */\\n struct AttestationData {\\n address about;\\n bytes32 key;\\n bytes val;\\n }\\n\\n /**\\n * @notice Maps addresses to attestations. Creator => About => Key => Value.\\n */\\n mapping(address => mapping(address => mapping(bytes32 => bytes))) public attestations;\\n\\n /**\\n * @notice Emitted when Attestation is created.\\n *\\n * @param creator Address that made the attestation.\\n * @param about Address attestation is about.\\n * @param key Key of the attestation.\\n * @param val Value of the attestation.\\n */\\n event AttestationCreated(\\n address indexed creator,\\n address indexed about,\\n bytes32 indexed key,\\n bytes val\\n );\\n\\n /**\\n * @custom:semver 1.1.0\\n */\\n constructor() Semver(1, 1, 0) {}\\n\\n /**\\n * @notice Allows anyone to create an attestation.\\n *\\n * @param _about Address that the attestation is about.\\n * @param _key A key used to namespace the attestation.\\n * @param _val An arbitrary value stored as part of the attestation.\\n */\\n function attest(\\n address _about,\\n bytes32 _key,\\n bytes memory _val\\n ) public {\\n attestations[msg.sender][_about][_key] = _val;\\n\\n emit AttestationCreated(msg.sender, _about, _key, _val);\\n }\\n\\n /**\\n * @notice Allows anyone to create attestations.\\n *\\n * @param _attestations An array of attestation data.\\n */\\n function attest(AttestationData[] calldata _attestations) external {\\n uint256 length = _attestations.length;\\n for (uint256 i = 0; i < length; ) {\\n AttestationData memory attestation = _attestations[i];\\n\\n attest(attestation.about, attestation.key, attestation.val);\\n\\n unchecked {\\n ++i;\\n }\\n }\\n }\\n}\\n\",\"keccak256\":\"0x80718fdc09061ea8f8b99a0b846eee46c5a2a1372dbb0cbc3d40953cb3af19e0\",\"license\":\"MIT\"}},\"version\":1}",
"bytecode": "0x60e060405234801561001057600080fd5b506001608081905260a052600060c05260805160a05160c051610b1c61004f60003960006101ad015260006101840152600061015b0152610b1c6000f3fe608060405234801561001057600080fd5b506004361061004c5760003560e01c806329b42cb51461005157806354fd4d501461007a5780635eb5ea1014610082578063702b9dee14610097575b600080fd5b61006461005f36600461046c565b6100aa565b604051610071919061051e565b60405180910390f35b610064610154565b610095610090366004610538565b6101f7565b005b6100956100a5366004610687565b61025a565b60006020818152938152604080822085529281528281209093528252902080546100d3906106de565b80601f01602080910402602001604051908101604052809291908181526020018280546100ff906106de565b801561014c5780601f106101215761010080835404028352916020019161014c565b820191906000526020600020905b81548152906001019060200180831161012f57829003601f168201915b505050505081565b606061017f7f0000000000000000000000000000000000000000000000000000000000000000610306565b6101a87f0000000000000000000000000000000000000000000000000000000000000000610306565b6101d17f0000000000000000000000000000000000000000000000000000000000000000610306565b6040516020016101e393929190610731565b604051602081830303815290604052905090565b8060005b81811015610254576000848483818110610217576102176107a7565b905060200281019061022991906107d6565b61023290610814565b905061024b81600001518260200151836040015161025a565b506001016101fb565b50505050565b3360009081526020818152604080832073ffffffffffffffffffffffffffffffffffffffff871684528252808320858452909152902061029a82826108df565b50818373ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff167f28710dfecab43d1e29e02aa56b2e1e610c0bae19135c9cf7a83a1adb6df96d85846040516102f9919061051e565b60405180910390a4505050565b60608160000361034957505060408051808201909152600181527f3000000000000000000000000000000000000000000000000000000000000000602082015290565b8160005b8115610373578061035d81610a28565b915061036c9050600a83610a8f565b915061034d565b60008167ffffffffffffffff81111561038e5761038e6105ad565b6040519080825280601f01601f1916602001820160405280156103b8576020820181803683370190505b5090505b841561043b576103cd600183610aa3565b91506103da600a86610aba565b6103e5906030610ace565b60f81b8183815181106103fa576103fa6107a7565b60200101907effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff1916908160001a905350610434600a86610a8f565b94506103bc565b949350505050565b803573ffffffffffffffffffffffffffffffffffffffff8116811461046757600080fd5b919050565b60008060006060848603121561048157600080fd5b61048a84610443565b925061049860208501610443565b9150604084013590509250925092565b60005b838110156104c35781810151838201526020016104ab565b838111156102545750506000910152565b600081518084526104ec8160208601602086016104a8565b601f017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0169290920160200192915050565b60208152600061053160208301846104d4565b9392505050565b6000806020838503121561054b57600080fd5b823567ffffffffffffffff8082111561056357600080fd5b818501915085601f83011261057757600080fd5b81358181111561058657600080fd5b8660208260051b850101111561059b57600080fd5b60209290920196919550909350505050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b600082601f8301126105ed57600080fd5b813567ffffffffffffffff80821115610608576106086105ad565b604051601f83017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0908116603f0116810190828211818310171561064e5761064e6105ad565b8160405283815286602085880101111561066757600080fd5b836020870160208301376000602085830101528094505050505092915050565b60008060006060848603121561069c57600080fd5b6106a584610443565b925060208401359150604084013567ffffffffffffffff8111156106c857600080fd5b6106d4868287016105dc565b9150509250925092565b600181811c908216806106f257607f821691505b60208210810361072b577f4e487b7100000000000000000000000000000000000000000000000000000000600052602260045260246000fd5b50919050565b600084516107438184602089016104a8565b80830190507f2e00000000000000000000000000000000000000000000000000000000000000808252855161077f816001850160208a016104a8565b6001920191820152835161079a8160028401602088016104a8565b0160020195945050505050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052603260045260246000fd5b600082357fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffa183360301811261080a57600080fd5b9190910192915050565b60006060823603121561082657600080fd5b6040516060810167ffffffffffffffff828210818311171561084a5761084a6105ad565b8160405261085785610443565b835260208501356020840152604085013591508082111561087757600080fd5b50610884368286016105dc565b60408301525092915050565b601f8211156108da57600081815260208120601f850160051c810160208610156108b75750805b601f850160051c820191505b818110156108d6578281556001016108c3565b5050505b505050565b815167ffffffffffffffff8111156108f9576108f96105ad565b61090d8161090784546106de565b84610890565b602080601f831160018114610960576000841561092a5750858301515b7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff600386901b1c1916600185901b1785556108d6565b6000858152602081207fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe08616915b828110156109ad5788860151825594840194600190910190840161098e565b50858210156109e957878501517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff600388901b60f8161c191681555b5050505050600190811b01905550565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601160045260246000fd5b60007fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff8203610a5957610a596109f9565b5060010190565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601260045260246000fd5b600082610a9e57610a9e610a60565b500490565b600082821015610ab557610ab56109f9565b500390565b600082610ac957610ac9610a60565b500690565b60008219821115610ae157610ae16109f9565b50019056fea2646970667358221220bfd4abacc1c2510682b093eea8745255cdd9f45c5b16c168540522a65ddc1f3264736f6c634300080f0033",
"deployedBytecode": "0x608060405234801561001057600080fd5b506004361061004c5760003560e01c806329b42cb51461005157806354fd4d501461007a5780635eb5ea1014610082578063702b9dee14610097575b600080fd5b61006461005f36600461046c565b6100aa565b604051610071919061051e565b60405180910390f35b610064610154565b610095610090366004610538565b6101f7565b005b6100956100a5366004610687565b61025a565b60006020818152938152604080822085529281528281209093528252902080546100d3906106de565b80601f01602080910402602001604051908101604052809291908181526020018280546100ff906106de565b801561014c5780601f106101215761010080835404028352916020019161014c565b820191906000526020600020905b81548152906001019060200180831161012f57829003601f168201915b505050505081565b606061017f7f0000000000000000000000000000000000000000000000000000000000000000610306565b6101a87f0000000000000000000000000000000000000000000000000000000000000000610306565b6101d17f0000000000000000000000000000000000000000000000000000000000000000610306565b6040516020016101e393929190610731565b604051602081830303815290604052905090565b8060005b81811015610254576000848483818110610217576102176107a7565b905060200281019061022991906107d6565b61023290610814565b905061024b81600001518260200151836040015161025a565b506001016101fb565b50505050565b3360009081526020818152604080832073ffffffffffffffffffffffffffffffffffffffff871684528252808320858452909152902061029a82826108df565b50818373ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff167f28710dfecab43d1e29e02aa56b2e1e610c0bae19135c9cf7a83a1adb6df96d85846040516102f9919061051e565b60405180910390a4505050565b60608160000361034957505060408051808201909152600181527f3000000000000000000000000000000000000000000000000000000000000000602082015290565b8160005b8115610373578061035d81610a28565b915061036c9050600a83610a8f565b915061034d565b60008167ffffffffffffffff81111561038e5761038e6105ad565b6040519080825280601f01601f1916602001820160405280156103b8576020820181803683370190505b5090505b841561043b576103cd600183610aa3565b91506103da600a86610aba565b6103e5906030610ace565b60f81b8183815181106103fa576103fa6107a7565b60200101907effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff1916908160001a905350610434600a86610a8f565b94506103bc565b949350505050565b803573ffffffffffffffffffffffffffffffffffffffff8116811461046757600080fd5b919050565b60008060006060848603121561048157600080fd5b61048a84610443565b925061049860208501610443565b9150604084013590509250925092565b60005b838110156104c35781810151838201526020016104ab565b838111156102545750506000910152565b600081518084526104ec8160208601602086016104a8565b601f017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0169290920160200192915050565b60208152600061053160208301846104d4565b9392505050565b6000806020838503121561054b57600080fd5b823567ffffffffffffffff8082111561056357600080fd5b818501915085601f83011261057757600080fd5b81358181111561058657600080fd5b8660208260051b850101111561059b57600080fd5b60209290920196919550909350505050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b600082601f8301126105ed57600080fd5b813567ffffffffffffffff80821115610608576106086105ad565b604051601f83017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0908116603f0116810190828211818310171561064e5761064e6105ad565b8160405283815286602085880101111561066757600080fd5b836020870160208301376000602085830101528094505050505092915050565b60008060006060848603121561069c57600080fd5b6106a584610443565b925060208401359150604084013567ffffffffffffffff8111156106c857600080fd5b6106d4868287016105dc565b9150509250925092565b600181811c908216806106f257607f821691505b60208210810361072b577f4e487b7100000000000000000000000000000000000000000000000000000000600052602260045260246000fd5b50919050565b600084516107438184602089016104a8565b80830190507f2e00000000000000000000000000000000000000000000000000000000000000808252855161077f816001850160208a016104a8565b6001920191820152835161079a8160028401602088016104a8565b0160020195945050505050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052603260045260246000fd5b600082357fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffa183360301811261080a57600080fd5b9190910192915050565b60006060823603121561082657600080fd5b6040516060810167ffffffffffffffff828210818311171561084a5761084a6105ad565b8160405261085785610443565b835260208501356020840152604085013591508082111561087757600080fd5b50610884368286016105dc565b60408301525092915050565b601f8211156108da57600081815260208120601f850160051c810160208610156108b75750805b601f850160051c820191505b818110156108d6578281556001016108c3565b5050505b505050565b815167ffffffffffffffff8111156108f9576108f96105ad565b61090d8161090784546106de565b84610890565b602080601f831160018114610960576000841561092a5750858301515b7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff600386901b1c1916600185901b1785556108d6565b6000858152602081207fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe08616915b828110156109ad5788860151825594840194600190910190840161098e565b50858210156109e957878501517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff600388901b60f8161c191681555b5050505050600190811b01905550565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601160045260246000fd5b60007fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff8203610a5957610a596109f9565b5060010190565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601260045260246000fd5b600082610a9e57610a9e610a60565b500490565b600082821015610ab557610ab56109f9565b500390565b600082610ac957610ac9610a60565b500690565b60008219821115610ae157610ae16109f9565b50019056fea2646970667358221220bfd4abacc1c2510682b093eea8745255cdd9f45c5b16c168540522a65ddc1f3264736f6c634300080f0033",
"devdoc": {
"author": "Optimism CollectiveGitcoin",
"events": {
......@@ -151,8 +174,15 @@
"_attestations": "An array of attestation data."
}
},
"attest(address,bytes32,bytes)": {
"params": {
"_about": "Address that the attestation is about.",
"_key": "A key used to namespace the attestation.",
"_val": "An arbitrary value stored as part of the attestation."
}
},
"constructor": {
"custom:semver": "1.0.0"
"custom:semver": "1.1.0"
},
"version()": {
"returns": {
......@@ -174,6 +204,9 @@
"attest((address,bytes32,bytes)[])": {
"notice": "Allows anyone to create attestations."
},
"attest(address,bytes32,bytes)": {
"notice": "Allows anyone to create an attestation."
},
"attestations(address,address,bytes32)": {
"notice": "Maps addresses to attestations. Creator => About => Key => Value."
},
......@@ -187,7 +220,7 @@
"storageLayout": {
"storage": [
{
"astId": 8191,
"astId": 2135,
"contract": "contracts/universal/op-nft/AttestationStation.sol:AttestationStation",
"label": "attestations",
"offset": 0,
......
{
"language": "Solidity",
"sources": {
"contracts/universal/op-nft/AttestationStation.sol": {
"content": "// SPDX-License-Identifier: MIT\npragma solidity 0.8.15;\n\nimport { Semver } from \"@eth-optimism/contracts-bedrock/contracts/universal/Semver.sol\";\n\n/**\n * @title AttestationStation\n * @author Optimism Collective\n * @author Gitcoin\n * @notice Where attestations live.\n */\ncontract AttestationStation is Semver {\n /**\n * @notice Struct representing data that is being attested.\n *\n * @custom:field about Address for which the attestation is about.\n * @custom:field key A bytes32 key for the attestation.\n * @custom:field val The attestation as arbitrary bytes.\n */\n struct AttestationData {\n address about;\n bytes32 key;\n bytes val;\n }\n\n /**\n * @notice Maps addresses to attestations. Creator => About => Key => Value.\n */\n mapping(address => mapping(address => mapping(bytes32 => bytes))) public attestations;\n\n /**\n * @notice Emitted when Attestation is created.\n *\n * @param creator Address that made the attestation.\n * @param about Address attestation is about.\n * @param key Key of the attestation.\n * @param val Value of the attestation.\n */\n event AttestationCreated(\n address indexed creator,\n address indexed about,\n bytes32 indexed key,\n bytes val\n );\n\n /**\n * @custom:semver 1.1.0\n */\n constructor() Semver(1, 1, 0) {}\n\n /**\n * @notice Allows anyone to create an attestation.\n *\n * @param _about Address that the attestation is about.\n * @param _key A key used to namespace the attestation.\n * @param _val An arbitrary value stored as part of the attestation.\n */\n function attest(\n address _about,\n bytes32 _key,\n bytes memory _val\n ) public {\n attestations[msg.sender][_about][_key] = _val;\n\n emit AttestationCreated(msg.sender, _about, _key, _val);\n }\n\n /**\n * @notice Allows anyone to create attestations.\n *\n * @param _attestations An array of attestation data.\n */\n function attest(AttestationData[] calldata _attestations) external {\n uint256 length = _attestations.length;\n for (uint256 i = 0; i < length; ) {\n AttestationData memory attestation = _attestations[i];\n\n attest(attestation.about, attestation.key, attestation.val);\n\n unchecked {\n ++i;\n }\n }\n }\n}\n"
},
"@eth-optimism/contracts-bedrock/contracts/universal/Semver.sol": {
"content": "// SPDX-License-Identifier: MIT\npragma solidity ^0.8.15;\n\nimport { Strings } from \"@openzeppelin/contracts/utils/Strings.sol\";\n\n/**\n * @title Semver\n * @notice Semver is a simple contract for managing contract versions.\n */\ncontract Semver {\n /**\n * @notice Contract version number (major).\n */\n uint256 private immutable MAJOR_VERSION;\n\n /**\n * @notice Contract version number (minor).\n */\n uint256 private immutable MINOR_VERSION;\n\n /**\n * @notice Contract version number (patch).\n */\n uint256 private immutable PATCH_VERSION;\n\n /**\n * @param _major Version number (major).\n * @param _minor Version number (minor).\n * @param _patch Version number (patch).\n */\n constructor(\n uint256 _major,\n uint256 _minor,\n uint256 _patch\n ) {\n MAJOR_VERSION = _major;\n MINOR_VERSION = _minor;\n PATCH_VERSION = _patch;\n }\n\n /**\n * @notice Returns the full semver contract version.\n *\n * @return Semver contract version as a string.\n */\n function version() public view returns (string memory) {\n return\n string(\n abi.encodePacked(\n Strings.toString(MAJOR_VERSION),\n \".\",\n Strings.toString(MINOR_VERSION),\n \".\",\n Strings.toString(PATCH_VERSION)\n )\n );\n }\n}\n"
},
"@openzeppelin/contracts/utils/Strings.sol": {
"content": "// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts (last updated v4.7.0) (utils/Strings.sol)\n\npragma solidity ^0.8.0;\n\n/**\n * @dev String operations.\n */\nlibrary Strings {\n bytes16 private constant _HEX_SYMBOLS = \"0123456789abcdef\";\n uint8 private constant _ADDRESS_LENGTH = 20;\n\n /**\n * @dev Converts a `uint256` to its ASCII `string` decimal representation.\n */\n function toString(uint256 value) internal pure returns (string memory) {\n // Inspired by OraclizeAPI's implementation - MIT licence\n // https://github.com/oraclize/ethereum-api/blob/b42146b063c7d6ee1358846c198246239e9360e8/oraclizeAPI_0.4.25.sol\n\n if (value == 0) {\n return \"0\";\n }\n uint256 temp = value;\n uint256 digits;\n while (temp != 0) {\n digits++;\n temp /= 10;\n }\n bytes memory buffer = new bytes(digits);\n while (value != 0) {\n digits -= 1;\n buffer[digits] = bytes1(uint8(48 + uint256(value % 10)));\n value /= 10;\n }\n return string(buffer);\n }\n\n /**\n * @dev Converts a `uint256` to its ASCII `string` hexadecimal representation.\n */\n function toHexString(uint256 value) internal pure returns (string memory) {\n if (value == 0) {\n return \"0x00\";\n }\n uint256 temp = value;\n uint256 length = 0;\n while (temp != 0) {\n length++;\n temp >>= 8;\n }\n return toHexString(value, length);\n }\n\n /**\n * @dev Converts a `uint256` to its ASCII `string` hexadecimal representation with fixed length.\n */\n function toHexString(uint256 value, uint256 length) internal pure returns (string memory) {\n bytes memory buffer = new bytes(2 * length + 2);\n buffer[0] = \"0\";\n buffer[1] = \"x\";\n for (uint256 i = 2 * length + 1; i > 1; --i) {\n buffer[i] = _HEX_SYMBOLS[value & 0xf];\n value >>= 4;\n }\n require(value == 0, \"Strings: hex length insufficient\");\n return string(buffer);\n }\n\n /**\n * @dev Converts an `address` with fixed length of 20 bytes to its not checksummed ASCII `string` hexadecimal representation.\n */\n function toHexString(address addr) internal pure returns (string memory) {\n return toHexString(uint256(uint160(addr)), _ADDRESS_LENGTH);\n }\n}\n"
},
"contracts/universal/op-nft/Optimist.sol": {
"content": "// SPDX-License-Identifier: MIT\npragma solidity 0.8.15;\n\nimport { Semver } from \"@eth-optimism/contracts-bedrock/contracts/universal/Semver.sol\";\nimport {\n ERC721BurnableUpgradeable\n} from \"@openzeppelin/contracts-upgradeable/token/ERC721/extensions/ERC721BurnableUpgradeable.sol\";\nimport { AttestationStation } from \"./AttestationStation.sol\";\nimport { Strings } from \"@openzeppelin/contracts/utils/Strings.sol\";\n\n/**\n * @author Optimism Collective\n * @author Gitcoin\n * @title Optimist\n * @notice A Soul Bound Token for real humans only(tm).\n */\ncontract Optimist is ERC721BurnableUpgradeable, Semver {\n /**\n * @notice Address of the AttestationStation contract.\n */\n AttestationStation public immutable ATTESTATION_STATION;\n\n /**\n * @notice Attestor who attests to baseURI and allowlist.\n */\n address public immutable ATTESTOR;\n\n /**\n * @custom:semver 1.0.0\n * @param _name Token name.\n * @param _symbol Token symbol.\n * @param _attestor Address of the attestor.\n * @param _attestationStation Address of the AttestationStation contract.\n */\n constructor(\n string memory _name,\n string memory _symbol,\n address _attestor,\n AttestationStation _attestationStation\n ) Semver(1, 0, 0) {\n ATTESTOR = _attestor;\n ATTESTATION_STATION = _attestationStation;\n initialize(_name, _symbol);\n }\n\n /**\n * @notice Initializes the Optimist contract.\n *\n * @param _name Token name.\n * @param _symbol Token symbol.\n */\n function initialize(string memory _name, string memory _symbol) public initializer {\n __ERC721_init(_name, _symbol);\n __ERC721Burnable_init();\n }\n\n /**\n * @notice Allows an address to mint an Optimist NFT. Token ID is the uint256 representation\n * of the recipient's address. Recipients must be permitted to mint, eventually anyone\n * will be able to mint. One token per address.\n *\n * @param _recipient Address of the token recipient.\n */\n function mint(address _recipient) public {\n require(isOnAllowList(_recipient), \"Optimist: address is not on allowList\");\n _safeMint(_recipient, tokenIdOfAddress(_recipient));\n }\n\n /**\n * @notice Returns the baseURI for all tokens.\n *\n * @return BaseURI for all tokens.\n */\n function baseURI() public view returns (string memory) {\n return\n string(\n abi.encodePacked(\n ATTESTATION_STATION.attestations(\n ATTESTOR,\n address(this),\n bytes32(\"optimist.base-uri\")\n )\n )\n );\n }\n\n /**\n * @notice Returns the token URI for a given token by ID\n *\n * @param _tokenId Token ID to query.\n\n * @return Token URI for the given token by ID.\n */\n function tokenURI(uint256 _tokenId) public view virtual override returns (string memory) {\n return\n string(\n abi.encodePacked(\n baseURI(),\n \"/\",\n // Properly format the token ID as a 20 byte hex string (address).\n Strings.toHexString(_tokenId, 20),\n \".json\"\n )\n );\n }\n\n /**\n * @notice Checks whether a given address is allowed to mint the Optimist NFT yet. Since the\n * Optimist NFT will also be used as part of the Citizens House, mints are currently\n * restricted. Eventually anyone will be able to mint.\n *\n * @return Whether or not the address is allowed to mint yet.\n */\n function isOnAllowList(address _recipient) public view returns (bool) {\n return\n ATTESTATION_STATION\n .attestations(ATTESTOR, _recipient, bytes32(\"optimist.can-mint\"))\n .length > 0;\n }\n\n /**\n * @notice Returns the token ID for the token owned by a given address. This is the uint256\n * representation of the given address.\n *\n * @return Token ID for the token owned by the given address.\n */\n function tokenIdOfAddress(address _owner) public pure returns (uint256) {\n return uint256(uint160(_owner));\n }\n\n /**\n * @notice Disabled for the Optimist NFT (Soul Bound Token).\n */\n function approve(address, uint256) public pure override {\n revert(\"Optimist: soul bound token\");\n }\n\n /**\n * @notice Disabled for the Optimist NFT (Soul Bound Token).\n */\n function setApprovalForAll(address, bool) public virtual override {\n revert(\"Optimist: soul bound token\");\n }\n\n /**\n * @notice Prevents transfers of the Optimist NFT (Soul Bound Token).\n *\n * @param _from Address of the token sender.\n * @param _to Address of the token recipient.\n */\n function _beforeTokenTransfer(\n address _from,\n address _to,\n uint256\n ) internal virtual override {\n require(_from == address(0) || _to == address(0), \"Optimist: soul bound token\");\n }\n}\n"
},
"@openzeppelin/contracts-upgradeable/token/ERC721/extensions/ERC721BurnableUpgradeable.sol": {
"content": "// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts (last updated v4.7.0) (token/ERC721/extensions/ERC721Burnable.sol)\n\npragma solidity ^0.8.0;\n\nimport \"../ERC721Upgradeable.sol\";\nimport \"../../../utils/ContextUpgradeable.sol\";\nimport \"../../../proxy/utils/Initializable.sol\";\n\n/**\n * @title ERC721 Burnable Token\n * @dev ERC721 Token that can be burned (destroyed).\n */\nabstract contract ERC721BurnableUpgradeable is Initializable, ContextUpgradeable, ERC721Upgradeable {\n function __ERC721Burnable_init() internal onlyInitializing {\n }\n\n function __ERC721Burnable_init_unchained() internal onlyInitializing {\n }\n /**\n * @dev Burns `tokenId`. See {ERC721-_burn}.\n *\n * Requirements:\n *\n * - The caller must own `tokenId` or be an approved operator.\n */\n function burn(uint256 tokenId) public virtual {\n //solhint-disable-next-line max-line-length\n require(_isApprovedOrOwner(_msgSender(), tokenId), \"ERC721: caller is not token owner nor approved\");\n _burn(tokenId);\n }\n\n /**\n * @dev This empty reserved space is put in place to allow future versions to add new\n * variables without shifting down storage in the inheritance chain.\n * See https://docs.openzeppelin.com/contracts/4.x/upgradeable#storage_gaps\n */\n uint256[50] private __gap;\n}\n"
},
"@openzeppelin/contracts-upgradeable/token/ERC721/ERC721Upgradeable.sol": {
"content": "// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts (last updated v4.7.0) (token/ERC721/ERC721.sol)\n\npragma solidity ^0.8.0;\n\nimport \"./IERC721Upgradeable.sol\";\nimport \"./IERC721ReceiverUpgradeable.sol\";\nimport \"./extensions/IERC721MetadataUpgradeable.sol\";\nimport \"../../utils/AddressUpgradeable.sol\";\nimport \"../../utils/ContextUpgradeable.sol\";\nimport \"../../utils/StringsUpgradeable.sol\";\nimport \"../../utils/introspection/ERC165Upgradeable.sol\";\nimport \"../../proxy/utils/Initializable.sol\";\n\n/**\n * @dev Implementation of https://eips.ethereum.org/EIPS/eip-721[ERC721] Non-Fungible Token Standard, including\n * the Metadata extension, but not including the Enumerable extension, which is available separately as\n * {ERC721Enumerable}.\n */\ncontract ERC721Upgradeable is Initializable, ContextUpgradeable, ERC165Upgradeable, IERC721Upgradeable, IERC721MetadataUpgradeable {\n using AddressUpgradeable for address;\n using StringsUpgradeable for uint256;\n\n // Token name\n string private _name;\n\n // Token symbol\n string private _symbol;\n\n // Mapping from token ID to owner address\n mapping(uint256 => address) private _owners;\n\n // Mapping owner address to token count\n mapping(address => uint256) private _balances;\n\n // Mapping from token ID to approved address\n mapping(uint256 => address) private _tokenApprovals;\n\n // Mapping from owner to operator approvals\n mapping(address => mapping(address => bool)) private _operatorApprovals;\n\n /**\n * @dev Initializes the contract by setting a `name` and a `symbol` to the token collection.\n */\n function __ERC721_init(string memory name_, string memory symbol_) internal onlyInitializing {\n __ERC721_init_unchained(name_, symbol_);\n }\n\n function __ERC721_init_unchained(string memory name_, string memory symbol_) internal onlyInitializing {\n _name = name_;\n _symbol = symbol_;\n }\n\n /**\n * @dev See {IERC165-supportsInterface}.\n */\n function supportsInterface(bytes4 interfaceId) public view virtual override(ERC165Upgradeable, IERC165Upgradeable) returns (bool) {\n return\n interfaceId == type(IERC721Upgradeable).interfaceId ||\n interfaceId == type(IERC721MetadataUpgradeable).interfaceId ||\n super.supportsInterface(interfaceId);\n }\n\n /**\n * @dev See {IERC721-balanceOf}.\n */\n function balanceOf(address owner) public view virtual override returns (uint256) {\n require(owner != address(0), \"ERC721: address zero is not a valid owner\");\n return _balances[owner];\n }\n\n /**\n * @dev See {IERC721-ownerOf}.\n */\n function ownerOf(uint256 tokenId) public view virtual override returns (address) {\n address owner = _owners[tokenId];\n require(owner != address(0), \"ERC721: invalid token ID\");\n return owner;\n }\n\n /**\n * @dev See {IERC721Metadata-name}.\n */\n function name() public view virtual override returns (string memory) {\n return _name;\n }\n\n /**\n * @dev See {IERC721Metadata-symbol}.\n */\n function symbol() public view virtual override returns (string memory) {\n return _symbol;\n }\n\n /**\n * @dev See {IERC721Metadata-tokenURI}.\n */\n function tokenURI(uint256 tokenId) public view virtual override returns (string memory) {\n _requireMinted(tokenId);\n\n string memory baseURI = _baseURI();\n return bytes(baseURI).length > 0 ? string(abi.encodePacked(baseURI, tokenId.toString())) : \"\";\n }\n\n /**\n * @dev Base URI for computing {tokenURI}. If set, the resulting URI for each\n * token will be the concatenation of the `baseURI` and the `tokenId`. Empty\n * by default, can be overridden in child contracts.\n */\n function _baseURI() internal view virtual returns (string memory) {\n return \"\";\n }\n\n /**\n * @dev See {IERC721-approve}.\n */\n function approve(address to, uint256 tokenId) public virtual override {\n address owner = ERC721Upgradeable.ownerOf(tokenId);\n require(to != owner, \"ERC721: approval to current owner\");\n\n require(\n _msgSender() == owner || isApprovedForAll(owner, _msgSender()),\n \"ERC721: approve caller is not token owner nor approved for all\"\n );\n\n _approve(to, tokenId);\n }\n\n /**\n * @dev See {IERC721-getApproved}.\n */\n function getApproved(uint256 tokenId) public view virtual override returns (address) {\n _requireMinted(tokenId);\n\n return _tokenApprovals[tokenId];\n }\n\n /**\n * @dev See {IERC721-setApprovalForAll}.\n */\n function setApprovalForAll(address operator, bool approved) public virtual override {\n _setApprovalForAll(_msgSender(), operator, approved);\n }\n\n /**\n * @dev See {IERC721-isApprovedForAll}.\n */\n function isApprovedForAll(address owner, address operator) public view virtual override returns (bool) {\n return _operatorApprovals[owner][operator];\n }\n\n /**\n * @dev See {IERC721-transferFrom}.\n */\n function transferFrom(\n address from,\n address to,\n uint256 tokenId\n ) public virtual override {\n //solhint-disable-next-line max-line-length\n require(_isApprovedOrOwner(_msgSender(), tokenId), \"ERC721: caller is not token owner nor approved\");\n\n _transfer(from, to, tokenId);\n }\n\n /**\n * @dev See {IERC721-safeTransferFrom}.\n */\n function safeTransferFrom(\n address from,\n address to,\n uint256 tokenId\n ) public virtual override {\n safeTransferFrom(from, to, tokenId, \"\");\n }\n\n /**\n * @dev See {IERC721-safeTransferFrom}.\n */\n function safeTransferFrom(\n address from,\n address to,\n uint256 tokenId,\n bytes memory data\n ) public virtual override {\n require(_isApprovedOrOwner(_msgSender(), tokenId), \"ERC721: caller is not token owner nor approved\");\n _safeTransfer(from, to, tokenId, data);\n }\n\n /**\n * @dev Safely transfers `tokenId` token from `from` to `to`, checking first that contract recipients\n * are aware of the ERC721 protocol to prevent tokens from being forever locked.\n *\n * `data` is additional data, it has no specified format and it is sent in call to `to`.\n *\n * This internal function is equivalent to {safeTransferFrom}, and can be used to e.g.\n * implement alternative mechanisms to perform token transfer, such as signature-based.\n *\n * Requirements:\n *\n * - `from` cannot be the zero address.\n * - `to` cannot be the zero address.\n * - `tokenId` token must exist and be owned by `from`.\n * - If `to` refers to a smart contract, it must implement {IERC721Receiver-onERC721Received}, which is called upon a safe transfer.\n *\n * Emits a {Transfer} event.\n */\n function _safeTransfer(\n address from,\n address to,\n uint256 tokenId,\n bytes memory data\n ) internal virtual {\n _transfer(from, to, tokenId);\n require(_checkOnERC721Received(from, to, tokenId, data), \"ERC721: transfer to non ERC721Receiver implementer\");\n }\n\n /**\n * @dev Returns whether `tokenId` exists.\n *\n * Tokens can be managed by their owner or approved accounts via {approve} or {setApprovalForAll}.\n *\n * Tokens start existing when they are minted (`_mint`),\n * and stop existing when they are burned (`_burn`).\n */\n function _exists(uint256 tokenId) internal view virtual returns (bool) {\n return _owners[tokenId] != address(0);\n }\n\n /**\n * @dev Returns whether `spender` is allowed to manage `tokenId`.\n *\n * Requirements:\n *\n * - `tokenId` must exist.\n */\n function _isApprovedOrOwner(address spender, uint256 tokenId) internal view virtual returns (bool) {\n address owner = ERC721Upgradeable.ownerOf(tokenId);\n return (spender == owner || isApprovedForAll(owner, spender) || getApproved(tokenId) == spender);\n }\n\n /**\n * @dev Safely mints `tokenId` and transfers it to `to`.\n *\n * Requirements:\n *\n * - `tokenId` must not exist.\n * - If `to` refers to a smart contract, it must implement {IERC721Receiver-onERC721Received}, which is called upon a safe transfer.\n *\n * Emits a {Transfer} event.\n */\n function _safeMint(address to, uint256 tokenId) internal virtual {\n _safeMint(to, tokenId, \"\");\n }\n\n /**\n * @dev Same as {xref-ERC721-_safeMint-address-uint256-}[`_safeMint`], with an additional `data` parameter which is\n * forwarded in {IERC721Receiver-onERC721Received} to contract recipients.\n */\n function _safeMint(\n address to,\n uint256 tokenId,\n bytes memory data\n ) internal virtual {\n _mint(to, tokenId);\n require(\n _checkOnERC721Received(address(0), to, tokenId, data),\n \"ERC721: transfer to non ERC721Receiver implementer\"\n );\n }\n\n /**\n * @dev Mints `tokenId` and transfers it to `to`.\n *\n * WARNING: Usage of this method is discouraged, use {_safeMint} whenever possible\n *\n * Requirements:\n *\n * - `tokenId` must not exist.\n * - `to` cannot be the zero address.\n *\n * Emits a {Transfer} event.\n */\n function _mint(address to, uint256 tokenId) internal virtual {\n require(to != address(0), \"ERC721: mint to the zero address\");\n require(!_exists(tokenId), \"ERC721: token already minted\");\n\n _beforeTokenTransfer(address(0), to, tokenId);\n\n _balances[to] += 1;\n _owners[tokenId] = to;\n\n emit Transfer(address(0), to, tokenId);\n\n _afterTokenTransfer(address(0), to, tokenId);\n }\n\n /**\n * @dev Destroys `tokenId`.\n * The approval is cleared when the token is burned.\n *\n * Requirements:\n *\n * - `tokenId` must exist.\n *\n * Emits a {Transfer} event.\n */\n function _burn(uint256 tokenId) internal virtual {\n address owner = ERC721Upgradeable.ownerOf(tokenId);\n\n _beforeTokenTransfer(owner, address(0), tokenId);\n\n // Clear approvals\n _approve(address(0), tokenId);\n\n _balances[owner] -= 1;\n delete _owners[tokenId];\n\n emit Transfer(owner, address(0), tokenId);\n\n _afterTokenTransfer(owner, address(0), tokenId);\n }\n\n /**\n * @dev Transfers `tokenId` from `from` to `to`.\n * As opposed to {transferFrom}, this imposes no restrictions on msg.sender.\n *\n * Requirements:\n *\n * - `to` cannot be the zero address.\n * - `tokenId` token must be owned by `from`.\n *\n * Emits a {Transfer} event.\n */\n function _transfer(\n address from,\n address to,\n uint256 tokenId\n ) internal virtual {\n require(ERC721Upgradeable.ownerOf(tokenId) == from, \"ERC721: transfer from incorrect owner\");\n require(to != address(0), \"ERC721: transfer to the zero address\");\n\n _beforeTokenTransfer(from, to, tokenId);\n\n // Clear approvals from the previous owner\n _approve(address(0), tokenId);\n\n _balances[from] -= 1;\n _balances[to] += 1;\n _owners[tokenId] = to;\n\n emit Transfer(from, to, tokenId);\n\n _afterTokenTransfer(from, to, tokenId);\n }\n\n /**\n * @dev Approve `to` to operate on `tokenId`\n *\n * Emits an {Approval} event.\n */\n function _approve(address to, uint256 tokenId) internal virtual {\n _tokenApprovals[tokenId] = to;\n emit Approval(ERC721Upgradeable.ownerOf(tokenId), to, tokenId);\n }\n\n /**\n * @dev Approve `operator` to operate on all of `owner` tokens\n *\n * Emits an {ApprovalForAll} event.\n */\n function _setApprovalForAll(\n address owner,\n address operator,\n bool approved\n ) internal virtual {\n require(owner != operator, \"ERC721: approve to caller\");\n _operatorApprovals[owner][operator] = approved;\n emit ApprovalForAll(owner, operator, approved);\n }\n\n /**\n * @dev Reverts if the `tokenId` has not been minted yet.\n */\n function _requireMinted(uint256 tokenId) internal view virtual {\n require(_exists(tokenId), \"ERC721: invalid token ID\");\n }\n\n /**\n * @dev Internal function to invoke {IERC721Receiver-onERC721Received} on a target address.\n * The call is not executed if the target address is not a contract.\n *\n * @param from address representing the previous owner of the given token ID\n * @param to target address that will receive the tokens\n * @param tokenId uint256 ID of the token to be transferred\n * @param data bytes optional data to send along with the call\n * @return bool whether the call correctly returned the expected magic value\n */\n function _checkOnERC721Received(\n address from,\n address to,\n uint256 tokenId,\n bytes memory data\n ) private returns (bool) {\n if (to.isContract()) {\n try IERC721ReceiverUpgradeable(to).onERC721Received(_msgSender(), from, tokenId, data) returns (bytes4 retval) {\n return retval == IERC721ReceiverUpgradeable.onERC721Received.selector;\n } catch (bytes memory reason) {\n if (reason.length == 0) {\n revert(\"ERC721: transfer to non ERC721Receiver implementer\");\n } else {\n /// @solidity memory-safe-assembly\n assembly {\n revert(add(32, reason), mload(reason))\n }\n }\n }\n } else {\n return true;\n }\n }\n\n /**\n * @dev Hook that is called before any token transfer. This includes minting\n * and burning.\n *\n * Calling conditions:\n *\n * - When `from` and `to` are both non-zero, ``from``'s `tokenId` will be\n * transferred to `to`.\n * - When `from` is zero, `tokenId` will be minted for `to`.\n * - When `to` is zero, ``from``'s `tokenId` will be burned.\n * - `from` and `to` are never both zero.\n *\n * To learn more about hooks, head to xref:ROOT:extending-contracts.adoc#using-hooks[Using Hooks].\n */\n function _beforeTokenTransfer(\n address from,\n address to,\n uint256 tokenId\n ) internal virtual {}\n\n /**\n * @dev Hook that is called after any transfer of tokens. This includes\n * minting and burning.\n *\n * Calling conditions:\n *\n * - when `from` and `to` are both non-zero.\n * - `from` and `to` are never both zero.\n *\n * To learn more about hooks, head to xref:ROOT:extending-contracts.adoc#using-hooks[Using Hooks].\n */\n function _afterTokenTransfer(\n address from,\n address to,\n uint256 tokenId\n ) internal virtual {}\n\n /**\n * @dev This empty reserved space is put in place to allow future versions to add new\n * variables without shifting down storage in the inheritance chain.\n * See https://docs.openzeppelin.com/contracts/4.x/upgradeable#storage_gaps\n */\n uint256[44] private __gap;\n}\n"
},
"@openzeppelin/contracts-upgradeable/utils/ContextUpgradeable.sol": {
"content": "// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts v4.4.1 (utils/Context.sol)\n\npragma solidity ^0.8.0;\nimport \"../proxy/utils/Initializable.sol\";\n\n/**\n * @dev Provides information about the current execution context, including the\n * sender of the transaction and its data. While these are generally available\n * via msg.sender and msg.data, they should not be accessed in such a direct\n * manner, since when dealing with meta-transactions the account sending and\n * paying for execution may not be the actual sender (as far as an application\n * is concerned).\n *\n * This contract is only required for intermediate, library-like contracts.\n */\nabstract contract ContextUpgradeable is Initializable {\n function __Context_init() internal onlyInitializing {\n }\n\n function __Context_init_unchained() internal onlyInitializing {\n }\n function _msgSender() internal view virtual returns (address) {\n return msg.sender;\n }\n\n function _msgData() internal view virtual returns (bytes calldata) {\n return msg.data;\n }\n\n /**\n * @dev This empty reserved space is put in place to allow future versions to add new\n * variables without shifting down storage in the inheritance chain.\n * See https://docs.openzeppelin.com/contracts/4.x/upgradeable#storage_gaps\n */\n uint256[50] private __gap;\n}\n"
},
"@openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol": {
"content": "// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts (last updated v4.7.0) (proxy/utils/Initializable.sol)\n\npragma solidity ^0.8.2;\n\nimport \"../../utils/AddressUpgradeable.sol\";\n\n/**\n * @dev This is a base contract to aid in writing upgradeable contracts, or any kind of contract that will be deployed\n * behind a proxy. Since proxied contracts do not make use of a constructor, it's common to move constructor logic to an\n * external initializer function, usually called `initialize`. It then becomes necessary to protect this initializer\n * function so it can only be called once. The {initializer} modifier provided by this contract will have this effect.\n *\n * The initialization functions use a version number. Once a version number is used, it is consumed and cannot be\n * reused. This mechanism prevents re-execution of each \"step\" but allows the creation of new initialization steps in\n * case an upgrade adds a module that needs to be initialized.\n *\n * For example:\n *\n * [.hljs-theme-light.nopadding]\n * ```\n * contract MyToken is ERC20Upgradeable {\n * function initialize() initializer public {\n * __ERC20_init(\"MyToken\", \"MTK\");\n * }\n * }\n * contract MyTokenV2 is MyToken, ERC20PermitUpgradeable {\n * function initializeV2() reinitializer(2) public {\n * __ERC20Permit_init(\"MyToken\");\n * }\n * }\n * ```\n *\n * TIP: To avoid leaving the proxy in an uninitialized state, the initializer function should be called as early as\n * possible by providing the encoded function call as the `_data` argument to {ERC1967Proxy-constructor}.\n *\n * CAUTION: When used with inheritance, manual care must be taken to not invoke a parent initializer twice, or to ensure\n * that all initializers are idempotent. This is not verified automatically as constructors are by Solidity.\n *\n * [CAUTION]\n * ====\n * Avoid leaving a contract uninitialized.\n *\n * An uninitialized contract can be taken over by an attacker. This applies to both a proxy and its implementation\n * contract, which may impact the proxy. To prevent the implementation contract from being used, you should invoke\n * the {_disableInitializers} function in the constructor to automatically lock it when it is deployed:\n *\n * [.hljs-theme-light.nopadding]\n * ```\n * /// @custom:oz-upgrades-unsafe-allow constructor\n * constructor() {\n * _disableInitializers();\n * }\n * ```\n * ====\n */\nabstract contract Initializable {\n /**\n * @dev Indicates that the contract has been initialized.\n * @custom:oz-retyped-from bool\n */\n uint8 private _initialized;\n\n /**\n * @dev Indicates that the contract is in the process of being initialized.\n */\n bool private _initializing;\n\n /**\n * @dev Triggered when the contract has been initialized or reinitialized.\n */\n event Initialized(uint8 version);\n\n /**\n * @dev A modifier that defines a protected initializer function that can be invoked at most once. In its scope,\n * `onlyInitializing` functions can be used to initialize parent contracts. Equivalent to `reinitializer(1)`.\n */\n modifier initializer() {\n bool isTopLevelCall = !_initializing;\n require(\n (isTopLevelCall && _initialized < 1) || (!AddressUpgradeable.isContract(address(this)) && _initialized == 1),\n \"Initializable: contract is already initialized\"\n );\n _initialized = 1;\n if (isTopLevelCall) {\n _initializing = true;\n }\n _;\n if (isTopLevelCall) {\n _initializing = false;\n emit Initialized(1);\n }\n }\n\n /**\n * @dev A modifier that defines a protected reinitializer function that can be invoked at most once, and only if the\n * contract hasn't been initialized to a greater version before. In its scope, `onlyInitializing` functions can be\n * used to initialize parent contracts.\n *\n * `initializer` is equivalent to `reinitializer(1)`, so a reinitializer may be used after the original\n * initialization step. This is essential to configure modules that are added through upgrades and that require\n * initialization.\n *\n * Note that versions can jump in increments greater than 1; this implies that if multiple reinitializers coexist in\n * a contract, executing them in the right order is up to the developer or operator.\n */\n modifier reinitializer(uint8 version) {\n require(!_initializing && _initialized < version, \"Initializable: contract is already initialized\");\n _initialized = version;\n _initializing = true;\n _;\n _initializing = false;\n emit Initialized(version);\n }\n\n /**\n * @dev Modifier to protect an initialization function so that it can only be invoked by functions with the\n * {initializer} and {reinitializer} modifiers, directly or indirectly.\n */\n modifier onlyInitializing() {\n require(_initializing, \"Initializable: contract is not initializing\");\n _;\n }\n\n /**\n * @dev Locks the contract, preventing any future reinitialization. This cannot be part of an initializer call.\n * Calling this in the constructor of a contract will prevent that contract from being initialized or reinitialized\n * to any version. It is recommended to use this to lock implementation contracts that are designed to be called\n * through proxies.\n */\n function _disableInitializers() internal virtual {\n require(!_initializing, \"Initializable: contract is initializing\");\n if (_initialized < type(uint8).max) {\n _initialized = type(uint8).max;\n emit Initialized(type(uint8).max);\n }\n }\n}\n"
},
"@openzeppelin/contracts-upgradeable/token/ERC721/IERC721Upgradeable.sol": {
"content": "// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts (last updated v4.7.0) (token/ERC721/IERC721.sol)\n\npragma solidity ^0.8.0;\n\nimport \"../../utils/introspection/IERC165Upgradeable.sol\";\n\n/**\n * @dev Required interface of an ERC721 compliant contract.\n */\ninterface IERC721Upgradeable is IERC165Upgradeable {\n /**\n * @dev Emitted when `tokenId` token is transferred from `from` to `to`.\n */\n event Transfer(address indexed from, address indexed to, uint256 indexed tokenId);\n\n /**\n * @dev Emitted when `owner` enables `approved` to manage the `tokenId` token.\n */\n event Approval(address indexed owner, address indexed approved, uint256 indexed tokenId);\n\n /**\n * @dev Emitted when `owner` enables or disables (`approved`) `operator` to manage all of its assets.\n */\n event ApprovalForAll(address indexed owner, address indexed operator, bool approved);\n\n /**\n * @dev Returns the number of tokens in ``owner``'s account.\n */\n function balanceOf(address owner) external view returns (uint256 balance);\n\n /**\n * @dev Returns the owner of the `tokenId` token.\n *\n * Requirements:\n *\n * - `tokenId` must exist.\n */\n function ownerOf(uint256 tokenId) external view returns (address owner);\n\n /**\n * @dev Safely transfers `tokenId` token from `from` to `to`.\n *\n * Requirements:\n *\n * - `from` cannot be the zero address.\n * - `to` cannot be the zero address.\n * - `tokenId` token must exist and be owned by `from`.\n * - If the caller is not `from`, it must be approved to move this token by either {approve} or {setApprovalForAll}.\n * - If `to` refers to a smart contract, it must implement {IERC721Receiver-onERC721Received}, which is called upon a safe transfer.\n *\n * Emits a {Transfer} event.\n */\n function safeTransferFrom(\n address from,\n address to,\n uint256 tokenId,\n bytes calldata data\n ) external;\n\n /**\n * @dev Safely transfers `tokenId` token from `from` to `to`, checking first that contract recipients\n * are aware of the ERC721 protocol to prevent tokens from being forever locked.\n *\n * Requirements:\n *\n * - `from` cannot be the zero address.\n * - `to` cannot be the zero address.\n * - `tokenId` token must exist and be owned by `from`.\n * - If the caller is not `from`, it must have been allowed to move this token by either {approve} or {setApprovalForAll}.\n * - If `to` refers to a smart contract, it must implement {IERC721Receiver-onERC721Received}, which is called upon a safe transfer.\n *\n * Emits a {Transfer} event.\n */\n function safeTransferFrom(\n address from,\n address to,\n uint256 tokenId\n ) external;\n\n /**\n * @dev Transfers `tokenId` token from `from` to `to`.\n *\n * WARNING: Usage of this method is discouraged, use {safeTransferFrom} whenever possible.\n *\n * Requirements:\n *\n * - `from` cannot be the zero address.\n * - `to` cannot be the zero address.\n * - `tokenId` token must be owned by `from`.\n * - If the caller is not `from`, it must be approved to move this token by either {approve} or {setApprovalForAll}.\n *\n * Emits a {Transfer} event.\n */\n function transferFrom(\n address from,\n address to,\n uint256 tokenId\n ) external;\n\n /**\n * @dev Gives permission to `to` to transfer `tokenId` token to another account.\n * The approval is cleared when the token is transferred.\n *\n * Only a single account can be approved at a time, so approving the zero address clears previous approvals.\n *\n * Requirements:\n *\n * - The caller must own the token or be an approved operator.\n * - `tokenId` must exist.\n *\n * Emits an {Approval} event.\n */\n function approve(address to, uint256 tokenId) external;\n\n /**\n * @dev Approve or remove `operator` as an operator for the caller.\n * Operators can call {transferFrom} or {safeTransferFrom} for any token owned by the caller.\n *\n * Requirements:\n *\n * - The `operator` cannot be the caller.\n *\n * Emits an {ApprovalForAll} event.\n */\n function setApprovalForAll(address operator, bool _approved) external;\n\n /**\n * @dev Returns the account approved for `tokenId` token.\n *\n * Requirements:\n *\n * - `tokenId` must exist.\n */\n function getApproved(uint256 tokenId) external view returns (address operator);\n\n /**\n * @dev Returns if the `operator` is allowed to manage all of the assets of `owner`.\n *\n * See {setApprovalForAll}\n */\n function isApprovedForAll(address owner, address operator) external view returns (bool);\n}\n"
},
"@openzeppelin/contracts-upgradeable/token/ERC721/IERC721ReceiverUpgradeable.sol": {
"content": "// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts (last updated v4.6.0) (token/ERC721/IERC721Receiver.sol)\n\npragma solidity ^0.8.0;\n\n/**\n * @title ERC721 token receiver interface\n * @dev Interface for any contract that wants to support safeTransfers\n * from ERC721 asset contracts.\n */\ninterface IERC721ReceiverUpgradeable {\n /**\n * @dev Whenever an {IERC721} `tokenId` token is transferred to this contract via {IERC721-safeTransferFrom}\n * by `operator` from `from`, this function is called.\n *\n * It must return its Solidity selector to confirm the token transfer.\n * If any other value is returned or the interface is not implemented by the recipient, the transfer will be reverted.\n *\n * The selector can be obtained in Solidity with `IERC721Receiver.onERC721Received.selector`.\n */\n function onERC721Received(\n address operator,\n address from,\n uint256 tokenId,\n bytes calldata data\n ) external returns (bytes4);\n}\n"
},
"@openzeppelin/contracts-upgradeable/token/ERC721/extensions/IERC721MetadataUpgradeable.sol": {
"content": "// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts v4.4.1 (token/ERC721/extensions/IERC721Metadata.sol)\n\npragma solidity ^0.8.0;\n\nimport \"../IERC721Upgradeable.sol\";\n\n/**\n * @title ERC-721 Non-Fungible Token Standard, optional metadata extension\n * @dev See https://eips.ethereum.org/EIPS/eip-721\n */\ninterface IERC721MetadataUpgradeable is IERC721Upgradeable {\n /**\n * @dev Returns the token collection name.\n */\n function name() external view returns (string memory);\n\n /**\n * @dev Returns the token collection symbol.\n */\n function symbol() external view returns (string memory);\n\n /**\n * @dev Returns the Uniform Resource Identifier (URI) for `tokenId` token.\n */\n function tokenURI(uint256 tokenId) external view returns (string memory);\n}\n"
},
"@openzeppelin/contracts-upgradeable/utils/AddressUpgradeable.sol": {
"content": "// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts (last updated v4.7.0) (utils/Address.sol)\n\npragma solidity ^0.8.1;\n\n/**\n * @dev Collection of functions related to the address type\n */\nlibrary AddressUpgradeable {\n /**\n * @dev Returns true if `account` is a contract.\n *\n * [IMPORTANT]\n * ====\n * It is unsafe to assume that an address for which this function returns\n * false is an externally-owned account (EOA) and not a contract.\n *\n * Among others, `isContract` will return false for the following\n * types of addresses:\n *\n * - an externally-owned account\n * - a contract in construction\n * - an address where a contract will be created\n * - an address where a contract lived, but was destroyed\n * ====\n *\n * [IMPORTANT]\n * ====\n * You shouldn't rely on `isContract` to protect against flash loan attacks!\n *\n * Preventing calls from contracts is highly discouraged. It breaks composability, breaks support for smart wallets\n * like Gnosis Safe, and does not provide security since it can be circumvented by calling from a contract\n * constructor.\n * ====\n */\n function isContract(address account) internal view returns (bool) {\n // This method relies on extcodesize/address.code.length, which returns 0\n // for contracts in construction, since the code is only stored at the end\n // of the constructor execution.\n\n return account.code.length > 0;\n }\n\n /**\n * @dev Replacement for Solidity's `transfer`: sends `amount` wei to\n * `recipient`, forwarding all available gas and reverting on errors.\n *\n * https://eips.ethereum.org/EIPS/eip-1884[EIP1884] increases the gas cost\n * of certain opcodes, possibly making contracts go over the 2300 gas limit\n * imposed by `transfer`, making them unable to receive funds via\n * `transfer`. {sendValue} removes this limitation.\n *\n * https://diligence.consensys.net/posts/2019/09/stop-using-soliditys-transfer-now/[Learn more].\n *\n * IMPORTANT: because control is transferred to `recipient`, care must be\n * taken to not create reentrancy vulnerabilities. Consider using\n * {ReentrancyGuard} or the\n * https://solidity.readthedocs.io/en/v0.5.11/security-considerations.html#use-the-checks-effects-interactions-pattern[checks-effects-interactions pattern].\n */\n function sendValue(address payable recipient, uint256 amount) internal {\n require(address(this).balance >= amount, \"Address: insufficient balance\");\n\n (bool success, ) = recipient.call{value: amount}(\"\");\n require(success, \"Address: unable to send value, recipient may have reverted\");\n }\n\n /**\n * @dev Performs a Solidity function call using a low level `call`. A\n * plain `call` is an unsafe replacement for a function call: use this\n * function instead.\n *\n * If `target` reverts with a revert reason, it is bubbled up by this\n * function (like regular Solidity function calls).\n *\n * Returns the raw returned data. To convert to the expected return value,\n * use https://solidity.readthedocs.io/en/latest/units-and-global-variables.html?highlight=abi.decode#abi-encoding-and-decoding-functions[`abi.decode`].\n *\n * Requirements:\n *\n * - `target` must be a contract.\n * - calling `target` with `data` must not revert.\n *\n * _Available since v3.1._\n */\n function functionCall(address target, bytes memory data) internal returns (bytes memory) {\n return functionCall(target, data, \"Address: low-level call failed\");\n }\n\n /**\n * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], but with\n * `errorMessage` as a fallback revert reason when `target` reverts.\n *\n * _Available since v3.1._\n */\n function functionCall(\n address target,\n bytes memory data,\n string memory errorMessage\n ) internal returns (bytes memory) {\n return functionCallWithValue(target, data, 0, errorMessage);\n }\n\n /**\n * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],\n * but also transferring `value` wei to `target`.\n *\n * Requirements:\n *\n * - the calling contract must have an ETH balance of at least `value`.\n * - the called Solidity function must be `payable`.\n *\n * _Available since v3.1._\n */\n function functionCallWithValue(\n address target,\n bytes memory data,\n uint256 value\n ) internal returns (bytes memory) {\n return functionCallWithValue(target, data, value, \"Address: low-level call with value failed\");\n }\n\n /**\n * @dev Same as {xref-Address-functionCallWithValue-address-bytes-uint256-}[`functionCallWithValue`], but\n * with `errorMessage` as a fallback revert reason when `target` reverts.\n *\n * _Available since v3.1._\n */\n function functionCallWithValue(\n address target,\n bytes memory data,\n uint256 value,\n string memory errorMessage\n ) internal returns (bytes memory) {\n require(address(this).balance >= value, \"Address: insufficient balance for call\");\n require(isContract(target), \"Address: call to non-contract\");\n\n (bool success, bytes memory returndata) = target.call{value: value}(data);\n return verifyCallResult(success, returndata, errorMessage);\n }\n\n /**\n * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],\n * but performing a static call.\n *\n * _Available since v3.3._\n */\n function functionStaticCall(address target, bytes memory data) internal view returns (bytes memory) {\n return functionStaticCall(target, data, \"Address: low-level static call failed\");\n }\n\n /**\n * @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`],\n * but performing a static call.\n *\n * _Available since v3.3._\n */\n function functionStaticCall(\n address target,\n bytes memory data,\n string memory errorMessage\n ) internal view returns (bytes memory) {\n require(isContract(target), \"Address: static call to non-contract\");\n\n (bool success, bytes memory returndata) = target.staticcall(data);\n return verifyCallResult(success, returndata, errorMessage);\n }\n\n /**\n * @dev Tool to verifies that a low level call was successful, and revert if it wasn't, either by bubbling the\n * revert reason using the provided one.\n *\n * _Available since v4.3._\n */\n function verifyCallResult(\n bool success,\n bytes memory returndata,\n string memory errorMessage\n ) internal pure returns (bytes memory) {\n if (success) {\n return returndata;\n } else {\n // Look for revert reason and bubble it up if present\n if (returndata.length > 0) {\n // The easiest way to bubble the revert reason is using memory via assembly\n /// @solidity memory-safe-assembly\n assembly {\n let returndata_size := mload(returndata)\n revert(add(32, returndata), returndata_size)\n }\n } else {\n revert(errorMessage);\n }\n }\n }\n}\n"
},
"@openzeppelin/contracts-upgradeable/utils/StringsUpgradeable.sol": {
"content": "// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts (last updated v4.7.0) (utils/Strings.sol)\n\npragma solidity ^0.8.0;\n\n/**\n * @dev String operations.\n */\nlibrary StringsUpgradeable {\n bytes16 private constant _HEX_SYMBOLS = \"0123456789abcdef\";\n uint8 private constant _ADDRESS_LENGTH = 20;\n\n /**\n * @dev Converts a `uint256` to its ASCII `string` decimal representation.\n */\n function toString(uint256 value) internal pure returns (string memory) {\n // Inspired by OraclizeAPI's implementation - MIT licence\n // https://github.com/oraclize/ethereum-api/blob/b42146b063c7d6ee1358846c198246239e9360e8/oraclizeAPI_0.4.25.sol\n\n if (value == 0) {\n return \"0\";\n }\n uint256 temp = value;\n uint256 digits;\n while (temp != 0) {\n digits++;\n temp /= 10;\n }\n bytes memory buffer = new bytes(digits);\n while (value != 0) {\n digits -= 1;\n buffer[digits] = bytes1(uint8(48 + uint256(value % 10)));\n value /= 10;\n }\n return string(buffer);\n }\n\n /**\n * @dev Converts a `uint256` to its ASCII `string` hexadecimal representation.\n */\n function toHexString(uint256 value) internal pure returns (string memory) {\n if (value == 0) {\n return \"0x00\";\n }\n uint256 temp = value;\n uint256 length = 0;\n while (temp != 0) {\n length++;\n temp >>= 8;\n }\n return toHexString(value, length);\n }\n\n /**\n * @dev Converts a `uint256` to its ASCII `string` hexadecimal representation with fixed length.\n */\n function toHexString(uint256 value, uint256 length) internal pure returns (string memory) {\n bytes memory buffer = new bytes(2 * length + 2);\n buffer[0] = \"0\";\n buffer[1] = \"x\";\n for (uint256 i = 2 * length + 1; i > 1; --i) {\n buffer[i] = _HEX_SYMBOLS[value & 0xf];\n value >>= 4;\n }\n require(value == 0, \"Strings: hex length insufficient\");\n return string(buffer);\n }\n\n /**\n * @dev Converts an `address` with fixed length of 20 bytes to its not checksummed ASCII `string` hexadecimal representation.\n */\n function toHexString(address addr) internal pure returns (string memory) {\n return toHexString(uint256(uint160(addr)), _ADDRESS_LENGTH);\n }\n}\n"
},
"@openzeppelin/contracts-upgradeable/utils/introspection/ERC165Upgradeable.sol": {
"content": "// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts v4.4.1 (utils/introspection/ERC165.sol)\n\npragma solidity ^0.8.0;\n\nimport \"./IERC165Upgradeable.sol\";\nimport \"../../proxy/utils/Initializable.sol\";\n\n/**\n * @dev Implementation of the {IERC165} interface.\n *\n * Contracts that want to implement ERC165 should inherit from this contract and override {supportsInterface} to check\n * for the additional interface id that will be supported. For example:\n *\n * ```solidity\n * function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) {\n * return interfaceId == type(MyInterface).interfaceId || super.supportsInterface(interfaceId);\n * }\n * ```\n *\n * Alternatively, {ERC165Storage} provides an easier to use but more expensive implementation.\n */\nabstract contract ERC165Upgradeable is Initializable, IERC165Upgradeable {\n function __ERC165_init() internal onlyInitializing {\n }\n\n function __ERC165_init_unchained() internal onlyInitializing {\n }\n /**\n * @dev See {IERC165-supportsInterface}.\n */\n function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) {\n return interfaceId == type(IERC165Upgradeable).interfaceId;\n }\n\n /**\n * @dev This empty reserved space is put in place to allow future versions to add new\n * variables without shifting down storage in the inheritance chain.\n * See https://docs.openzeppelin.com/contracts/4.x/upgradeable#storage_gaps\n */\n uint256[50] private __gap;\n}\n"
},
"@openzeppelin/contracts-upgradeable/utils/introspection/IERC165Upgradeable.sol": {
"content": "// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts v4.4.1 (utils/introspection/IERC165.sol)\n\npragma solidity ^0.8.0;\n\n/**\n * @dev Interface of the ERC165 standard, as defined in the\n * https://eips.ethereum.org/EIPS/eip-165[EIP].\n *\n * Implementers can declare support of contract interfaces, which can then be\n * queried by others ({ERC165Checker}).\n *\n * For an implementation, see {ERC165}.\n */\ninterface IERC165Upgradeable {\n /**\n * @dev Returns true if this contract implements the interface defined by\n * `interfaceId`. See the corresponding\n * https://eips.ethereum.org/EIPS/eip-165#how-interfaces-are-identified[EIP section]\n * to learn more about how these ids are created.\n *\n * This function call must use less than 30 000 gas.\n */\n function supportsInterface(bytes4 interfaceId) external view returns (bool);\n}\n"
}
},
"settings": {
"optimizer": {
"enabled": true,
"runs": 10000
},
"outputSelection": {
"*": {
"*": [
"abi",
"evm.bytecode",
"evm.deployedBytecode",
"evm.methodIdentifiers",
"metadata",
"devdoc",
"userdoc",
"storageLayout",
"evm.gasEstimates"
],
"": [
"ast"
]
}
},
"metadata": {
"useLiteralContent": true
}
}
}
\ No newline at end of file
{
"address": "0x1BEb19F1685ddF2F774884902119Fa2FA5d8f509",
"address": "0xbeD744818e96AAD8a51324291a6f6Cb20A0c22be",
"abi": [
{
"inputs": [],
......@@ -67,6 +67,29 @@
"stateMutability": "nonpayable",
"type": "function"
},
{
"inputs": [
{
"internalType": "address",
"name": "_about",
"type": "address"
},
{
"internalType": "bytes32",
"name": "_key",
"type": "bytes32"
},
{
"internalType": "bytes",
"name": "_val",
"type": "bytes"
}
],
"name": "attest",
"outputs": [],
"stateMutability": "nonpayable",
"type": "function"
},
{
"inputs": [
{
......@@ -110,28 +133,28 @@
"type": "function"
}
],
"transactionHash": "0x84a56c4b4090faef9131650fbbcf38e128e9d98ce77a06ca2e7485c8a137b9a6",
"transactionHash": "0x0272013b1d20b4b8e2b8153867389c1c75cbbd5c50e564ee71a62dbfcc35f062",
"receipt": {
"to": "0x4e59b44847b379578588920cA78FbF26c0B4956C",
"from": "0x9C6373dE60c2D3297b18A8f964618ac46E011B58",
"from": "0x956a5152D0f498dBA0c5966577bb44262F8F7078",
"contractAddress": null,
"transactionIndex": 0,
"gasUsed": "645110",
"gasUsed": "666309",
"logsBloom": "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
"blockHash": "0xe588af2eaf9e982580a846beb0f8030a896400431219ce0deca38524defc6324",
"transactionHash": "0x84a56c4b4090faef9131650fbbcf38e128e9d98ce77a06ca2e7485c8a137b9a6",
"blockHash": "0x6d539badaadce9732f2f79e1ceaee94499dd0003d42862324ff334bd7e7e6e68",
"transactionHash": "0x0272013b1d20b4b8e2b8153867389c1c75cbbd5c50e564ee71a62dbfcc35f062",
"logs": [],
"blockNumber": 49669933,
"cumulativeGasUsed": "645110",
"blockNumber": 70643992,
"cumulativeGasUsed": "666309",
"status": 1,
"byzantium": true
},
"args": [],
"numDeployments": 1,
"solcInputHash": "45837d34ff24b9cb2ae34232b60ea874",
"metadata": "{\"compiler\":{\"version\":\"0.8.15+commit.e14f2714\"},\"language\":\"Solidity\",\"output\":{\"abi\":[{\"inputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"constructor\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"address\",\"name\":\"creator\",\"type\":\"address\"},{\"indexed\":true,\"internalType\":\"address\",\"name\":\"about\",\"type\":\"address\"},{\"indexed\":true,\"internalType\":\"bytes32\",\"name\":\"key\",\"type\":\"bytes32\"},{\"indexed\":false,\"internalType\":\"bytes\",\"name\":\"val\",\"type\":\"bytes\"}],\"name\":\"AttestationCreated\",\"type\":\"event\"},{\"inputs\":[{\"components\":[{\"internalType\":\"address\",\"name\":\"about\",\"type\":\"address\"},{\"internalType\":\"bytes32\",\"name\":\"key\",\"type\":\"bytes32\"},{\"internalType\":\"bytes\",\"name\":\"val\",\"type\":\"bytes\"}],\"internalType\":\"struct AttestationStation.AttestationData[]\",\"name\":\"_attestations\",\"type\":\"tuple[]\"}],\"name\":\"attest\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"},{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"},{\"internalType\":\"bytes32\",\"name\":\"\",\"type\":\"bytes32\"}],\"name\":\"attestations\",\"outputs\":[{\"internalType\":\"bytes\",\"name\":\"\",\"type\":\"bytes\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"version\",\"outputs\":[{\"internalType\":\"string\",\"name\":\"\",\"type\":\"string\"}],\"stateMutability\":\"view\",\"type\":\"function\"}],\"devdoc\":{\"author\":\"Optimism CollectiveGitcoin\",\"events\":{\"AttestationCreated(address,address,bytes32,bytes)\":{\"params\":{\"about\":\"Address attestation is about.\",\"creator\":\"Address that made the attestation.\",\"key\":\"Key of the attestation.\",\"val\":\"Value of the attestation.\"}}},\"kind\":\"dev\",\"methods\":{\"attest((address,bytes32,bytes)[])\":{\"params\":{\"_attestations\":\"An array of attestation data.\"}},\"constructor\":{\"custom:semver\":\"1.0.0\"},\"version()\":{\"returns\":{\"_0\":\"Semver contract version as a string.\"}}},\"title\":\"AttestationStation\",\"version\":1},\"userdoc\":{\"events\":{\"AttestationCreated(address,address,bytes32,bytes)\":{\"notice\":\"Emitted when Attestation is created.\"}},\"kind\":\"user\",\"methods\":{\"attest((address,bytes32,bytes)[])\":{\"notice\":\"Allows anyone to create attestations.\"},\"attestations(address,address,bytes32)\":{\"notice\":\"Maps addresses to attestations. Creator => About => Key => Value.\"},\"version()\":{\"notice\":\"Returns the full semver contract version.\"}},\"notice\":\"Where attestations live.\",\"version\":1}},\"settings\":{\"compilationTarget\":{\"contracts/universal/op-nft/AttestationStation.sol\":\"AttestationStation\"},\"evmVersion\":\"london\",\"libraries\":{},\"metadata\":{\"bytecodeHash\":\"ipfs\",\"useLiteralContent\":true},\"optimizer\":{\"enabled\":true,\"runs\":10000},\"remappings\":[]},\"sources\":{\"@eth-optimism/contracts-bedrock/contracts/universal/Semver.sol\":{\"content\":\"// SPDX-License-Identifier: MIT\\npragma solidity ^0.8.15;\\n\\nimport { Strings } from \\\"@openzeppelin/contracts/utils/Strings.sol\\\";\\n\\n/**\\n * @title Semver\\n * @notice Semver is a simple contract for managing contract versions.\\n */\\ncontract Semver {\\n /**\\n * @notice Contract version number (major).\\n */\\n uint256 private immutable MAJOR_VERSION;\\n\\n /**\\n * @notice Contract version number (minor).\\n */\\n uint256 private immutable MINOR_VERSION;\\n\\n /**\\n * @notice Contract version number (patch).\\n */\\n uint256 private immutable PATCH_VERSION;\\n\\n /**\\n * @param _major Version number (major).\\n * @param _minor Version number (minor).\\n * @param _patch Version number (patch).\\n */\\n constructor(\\n uint256 _major,\\n uint256 _minor,\\n uint256 _patch\\n ) {\\n MAJOR_VERSION = _major;\\n MINOR_VERSION = _minor;\\n PATCH_VERSION = _patch;\\n }\\n\\n /**\\n * @notice Returns the full semver contract version.\\n *\\n * @return Semver contract version as a string.\\n */\\n function version() public view returns (string memory) {\\n return\\n string(\\n abi.encodePacked(\\n Strings.toString(MAJOR_VERSION),\\n \\\".\\\",\\n Strings.toString(MINOR_VERSION),\\n \\\".\\\",\\n Strings.toString(PATCH_VERSION)\\n )\\n );\\n }\\n}\\n\",\"keccak256\":\"0x979b13465de4996a1105850abbf48abe7f71d5e18a8d4af318597ee14c165fdf\",\"license\":\"MIT\"},\"@openzeppelin/contracts/utils/Strings.sol\":{\"content\":\"// SPDX-License-Identifier: MIT\\n// OpenZeppelin Contracts (last updated v4.7.0) (utils/Strings.sol)\\n\\npragma solidity ^0.8.0;\\n\\n/**\\n * @dev String operations.\\n */\\nlibrary Strings {\\n bytes16 private constant _HEX_SYMBOLS = \\\"0123456789abcdef\\\";\\n uint8 private constant _ADDRESS_LENGTH = 20;\\n\\n /**\\n * @dev Converts a `uint256` to its ASCII `string` decimal representation.\\n */\\n function toString(uint256 value) internal pure returns (string memory) {\\n // Inspired by OraclizeAPI's implementation - MIT licence\\n // https://github.com/oraclize/ethereum-api/blob/b42146b063c7d6ee1358846c198246239e9360e8/oraclizeAPI_0.4.25.sol\\n\\n if (value == 0) {\\n return \\\"0\\\";\\n }\\n uint256 temp = value;\\n uint256 digits;\\n while (temp != 0) {\\n digits++;\\n temp /= 10;\\n }\\n bytes memory buffer = new bytes(digits);\\n while (value != 0) {\\n digits -= 1;\\n buffer[digits] = bytes1(uint8(48 + uint256(value % 10)));\\n value /= 10;\\n }\\n return string(buffer);\\n }\\n\\n /**\\n * @dev Converts a `uint256` to its ASCII `string` hexadecimal representation.\\n */\\n function toHexString(uint256 value) internal pure returns (string memory) {\\n if (value == 0) {\\n return \\\"0x00\\\";\\n }\\n uint256 temp = value;\\n uint256 length = 0;\\n while (temp != 0) {\\n length++;\\n temp >>= 8;\\n }\\n return toHexString(value, length);\\n }\\n\\n /**\\n * @dev Converts a `uint256` to its ASCII `string` hexadecimal representation with fixed length.\\n */\\n function toHexString(uint256 value, uint256 length) internal pure returns (string memory) {\\n bytes memory buffer = new bytes(2 * length + 2);\\n buffer[0] = \\\"0\\\";\\n buffer[1] = \\\"x\\\";\\n for (uint256 i = 2 * length + 1; i > 1; --i) {\\n buffer[i] = _HEX_SYMBOLS[value & 0xf];\\n value >>= 4;\\n }\\n require(value == 0, \\\"Strings: hex length insufficient\\\");\\n return string(buffer);\\n }\\n\\n /**\\n * @dev Converts an `address` with fixed length of 20 bytes to its not checksummed ASCII `string` hexadecimal representation.\\n */\\n function toHexString(address addr) internal pure returns (string memory) {\\n return toHexString(uint256(uint160(addr)), _ADDRESS_LENGTH);\\n }\\n}\\n\",\"keccak256\":\"0xaf159a8b1923ad2a26d516089bceca9bdeaeacd04be50983ea00ba63070f08a3\",\"license\":\"MIT\"},\"contracts/universal/op-nft/AttestationStation.sol\":{\"content\":\"// SPDX-License-Identifier: MIT\\npragma solidity 0.8.15;\\n\\nimport { Semver } from \\\"@eth-optimism/contracts-bedrock/contracts/universal/Semver.sol\\\";\\n\\n/**\\n * @title AttestationStation\\n * @author Optimism Collective\\n * @author Gitcoin\\n * @notice Where attestations live.\\n */\\ncontract AttestationStation is Semver {\\n /**\\n * @notice Struct representing data that is being attested.\\n *\\n * @custom:field about Address for which the attestation is about.\\n * @custom:field key A bytes32 key for the attestation.\\n * @custom:field val The attestation as arbitrary bytes.\\n */\\n struct AttestationData {\\n address about;\\n bytes32 key;\\n bytes val;\\n }\\n\\n /**\\n * @notice Maps addresses to attestations. Creator => About => Key => Value.\\n */\\n mapping(address => mapping(address => mapping(bytes32 => bytes))) public attestations;\\n\\n /**\\n * @notice Emitted when Attestation is created.\\n *\\n * @param creator Address that made the attestation.\\n * @param about Address attestation is about.\\n * @param key Key of the attestation.\\n * @param val Value of the attestation.\\n */\\n event AttestationCreated(\\n address indexed creator,\\n address indexed about,\\n bytes32 indexed key,\\n bytes val\\n );\\n\\n /**\\n * @custom:semver 1.0.0\\n */\\n constructor() Semver(1, 0, 0) {}\\n\\n /**\\n * @notice Allows anyone to create attestations.\\n *\\n * @param _attestations An array of attestation data.\\n */\\n function attest(AttestationData[] memory _attestations) public {\\n uint256 length = _attestations.length;\\n for (uint256 i = 0; i < length; ) {\\n AttestationData memory attestation = _attestations[i];\\n attestations[msg.sender][attestation.about][attestation.key] = attestation.val;\\n\\n emit AttestationCreated(\\n msg.sender,\\n attestation.about,\\n attestation.key,\\n attestation.val\\n );\\n\\n unchecked {\\n ++i;\\n }\\n }\\n }\\n}\\n\",\"keccak256\":\"0x670fde721c291828c3f6343a40bb315dbbcc5e4411125158892ffe54459d69b7\",\"license\":\"MIT\"}},\"version\":1}",
"bytecode": "0x60e060405234801561001057600080fd5b5060016080819052600060a081905260c081905280610aba61004a8239600061018f015260006101660152600061013d0152610aba6000f3fe608060405234801561001057600080fd5b50600436106100415760003560e01c806329b42cb51461004657806354fd4d501461006f5780635eb5ea1014610077575b600080fd5b610059610054366004610437565b61008c565b60405161006691906104ed565b60405180910390f35b610059610136565b61008a6100853660046105ae565b6101d9565b005b60006020818152938152604080822085529281528281209093528252902080546100b590610737565b80601f01602080910402602001604051908101604052809291908181526020018280546100e190610737565b801561012e5780601f106101035761010080835404028352916020019161012e565b820191906000526020600020905b81548152906001019060200180831161011157829003601f168201915b505050505081565b60606101617f00000000000000000000000000000000000000000000000000000000000000006102d1565b61018a7f00000000000000000000000000000000000000000000000000000000000000006102d1565b6101b37f00000000000000000000000000000000000000000000000000000000000000006102d1565b6040516020016101c59392919061078a565b604051602081830303815290604052905090565b805160005b818110156102cc5760008382815181106101fa576101fa610800565b602090810291909101810151604080820151336000908152808552828120845173ffffffffffffffffffffffffffffffffffffffff1682528552828120848601518252909452922090925090610250908261087d565b508060200151816000015173ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff167f28710dfecab43d1e29e02aa56b2e1e610c0bae19135c9cf7a83a1adb6df96d8584604001516040516102bb91906104ed565b60405180910390a4506001016101de565b505050565b60608160000361031457505060408051808201909152600181527f3000000000000000000000000000000000000000000000000000000000000000602082015290565b8160005b811561033e5780610328816109c6565b91506103379050600a83610a2d565b9150610318565b60008167ffffffffffffffff81111561035957610359610507565b6040519080825280601f01601f191660200182016040528015610383576020820181803683370190505b5090505b841561040657610398600183610a41565b91506103a5600a86610a58565b6103b0906030610a6c565b60f81b8183815181106103c5576103c5610800565b60200101907effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff1916908160001a9053506103ff600a86610a2d565b9450610387565b949350505050565b803573ffffffffffffffffffffffffffffffffffffffff8116811461043257600080fd5b919050565b60008060006060848603121561044c57600080fd5b6104558461040e565b92506104636020850161040e565b9150604084013590509250925092565b60005b8381101561048e578181015183820152602001610476565b8381111561049d576000848401525b50505050565b600081518084526104bb816020860160208601610473565b601f017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0169290920160200192915050565b60208152600061050060208301846104a3565b9392505050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b6040516060810167ffffffffffffffff8111828210171561055957610559610507565b60405290565b604051601f82017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe016810167ffffffffffffffff811182821017156105a6576105a6610507565b604052919050565b600060208083850312156105c157600080fd5b823567ffffffffffffffff808211156105d957600080fd5b818501915085601f8301126105ed57600080fd5b8135818111156105ff576105ff610507565b8060051b61060e85820161055f565b918252838101850191858101908984111561062857600080fd5b86860192505b8383101561072a578235858111156106465760008081fd5b860160607fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0828d03810182131561067d5760008081fd5b610685610536565b6106908b850161040e565b81526040848101358c8301529284013592898411156106af5760008081fd5b83850194508e603f8601126106c657600093508384fd5b8b8501359350898411156106dc576106dc610507565b6106ec8c84601f8701160161055f565b92508383528e818587010111156107035760008081fd5b838186018d85013760009383018c019390935291820152835250918601919086019061062e565b9998505050505050505050565b600181811c9082168061074b57607f821691505b602082108103610784577f4e487b7100000000000000000000000000000000000000000000000000000000600052602260045260246000fd5b50919050565b6000845161079c818460208901610473565b80830190507f2e0000000000000000000000000000000000000000000000000000000000000080825285516107d8816001850160208a01610473565b600192019182015283516107f3816002840160208801610473565b0160020195945050505050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052603260045260246000fd5b601f8211156102cc57600081815260208120601f850160051c810160208610156108565750805b601f850160051c820191505b8181101561087557828155600101610862565b505050505050565b815167ffffffffffffffff81111561089757610897610507565b6108ab816108a58454610737565b8461082f565b602080601f8311600181146108fe57600084156108c85750858301515b7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff600386901b1c1916600185901b178555610875565b6000858152602081207fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe08616915b8281101561094b5788860151825594840194600190910190840161092c565b508582101561098757878501517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff600388901b60f8161c191681555b5050505050600190811b01905550565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601160045260246000fd5b60007fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff82036109f7576109f7610997565b5060010190565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601260045260246000fd5b600082610a3c57610a3c6109fe565b500490565b600082821015610a5357610a53610997565b500390565b600082610a6757610a676109fe565b500690565b60008219821115610a7f57610a7f610997565b50019056fea26469706673582212204abcd56633387050a41a0ae027bbf9077ae2fb5080fa84124e2aa381adb3f98964736f6c634300080f0033",
"deployedBytecode": "0x608060405234801561001057600080fd5b50600436106100415760003560e01c806329b42cb51461004657806354fd4d501461006f5780635eb5ea1014610077575b600080fd5b610059610054366004610437565b61008c565b60405161006691906104ed565b60405180910390f35b610059610136565b61008a6100853660046105ae565b6101d9565b005b60006020818152938152604080822085529281528281209093528252902080546100b590610737565b80601f01602080910402602001604051908101604052809291908181526020018280546100e190610737565b801561012e5780601f106101035761010080835404028352916020019161012e565b820191906000526020600020905b81548152906001019060200180831161011157829003601f168201915b505050505081565b60606101617f00000000000000000000000000000000000000000000000000000000000000006102d1565b61018a7f00000000000000000000000000000000000000000000000000000000000000006102d1565b6101b37f00000000000000000000000000000000000000000000000000000000000000006102d1565b6040516020016101c59392919061078a565b604051602081830303815290604052905090565b805160005b818110156102cc5760008382815181106101fa576101fa610800565b602090810291909101810151604080820151336000908152808552828120845173ffffffffffffffffffffffffffffffffffffffff1682528552828120848601518252909452922090925090610250908261087d565b508060200151816000015173ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff167f28710dfecab43d1e29e02aa56b2e1e610c0bae19135c9cf7a83a1adb6df96d8584604001516040516102bb91906104ed565b60405180910390a4506001016101de565b505050565b60608160000361031457505060408051808201909152600181527f3000000000000000000000000000000000000000000000000000000000000000602082015290565b8160005b811561033e5780610328816109c6565b91506103379050600a83610a2d565b9150610318565b60008167ffffffffffffffff81111561035957610359610507565b6040519080825280601f01601f191660200182016040528015610383576020820181803683370190505b5090505b841561040657610398600183610a41565b91506103a5600a86610a58565b6103b0906030610a6c565b60f81b8183815181106103c5576103c5610800565b60200101907effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff1916908160001a9053506103ff600a86610a2d565b9450610387565b949350505050565b803573ffffffffffffffffffffffffffffffffffffffff8116811461043257600080fd5b919050565b60008060006060848603121561044c57600080fd5b6104558461040e565b92506104636020850161040e565b9150604084013590509250925092565b60005b8381101561048e578181015183820152602001610476565b8381111561049d576000848401525b50505050565b600081518084526104bb816020860160208601610473565b601f017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0169290920160200192915050565b60208152600061050060208301846104a3565b9392505050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b6040516060810167ffffffffffffffff8111828210171561055957610559610507565b60405290565b604051601f82017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe016810167ffffffffffffffff811182821017156105a6576105a6610507565b604052919050565b600060208083850312156105c157600080fd5b823567ffffffffffffffff808211156105d957600080fd5b818501915085601f8301126105ed57600080fd5b8135818111156105ff576105ff610507565b8060051b61060e85820161055f565b918252838101850191858101908984111561062857600080fd5b86860192505b8383101561072a578235858111156106465760008081fd5b860160607fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0828d03810182131561067d5760008081fd5b610685610536565b6106908b850161040e565b81526040848101358c8301529284013592898411156106af5760008081fd5b83850194508e603f8601126106c657600093508384fd5b8b8501359350898411156106dc576106dc610507565b6106ec8c84601f8701160161055f565b92508383528e818587010111156107035760008081fd5b838186018d85013760009383018c019390935291820152835250918601919086019061062e565b9998505050505050505050565b600181811c9082168061074b57607f821691505b602082108103610784577f4e487b7100000000000000000000000000000000000000000000000000000000600052602260045260246000fd5b50919050565b6000845161079c818460208901610473565b80830190507f2e0000000000000000000000000000000000000000000000000000000000000080825285516107d8816001850160208a01610473565b600192019182015283516107f3816002840160208801610473565b0160020195945050505050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052603260045260246000fd5b601f8211156102cc57600081815260208120601f850160051c810160208610156108565750805b601f850160051c820191505b8181101561087557828155600101610862565b505050505050565b815167ffffffffffffffff81111561089757610897610507565b6108ab816108a58454610737565b8461082f565b602080601f8311600181146108fe57600084156108c85750858301515b7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff600386901b1c1916600185901b178555610875565b6000858152602081207fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe08616915b8281101561094b5788860151825594840194600190910190840161092c565b508582101561098757878501517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff600388901b60f8161c191681555b5050505050600190811b01905550565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601160045260246000fd5b60007fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff82036109f7576109f7610997565b5060010190565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601260045260246000fd5b600082610a3c57610a3c6109fe565b500490565b600082821015610a5357610a53610997565b500390565b600082610a6757610a676109fe565b500690565b60008219821115610a7f57610a7f610997565b50019056fea26469706673582212204abcd56633387050a41a0ae027bbf9077ae2fb5080fa84124e2aa381adb3f98964736f6c634300080f0033",
"numDeployments": 2,
"solcInputHash": "3aa2ad7d005d9515a1f12df8da17d178",
"metadata": "{\"compiler\":{\"version\":\"0.8.15+commit.e14f2714\"},\"language\":\"Solidity\",\"output\":{\"abi\":[{\"inputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"constructor\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"address\",\"name\":\"creator\",\"type\":\"address\"},{\"indexed\":true,\"internalType\":\"address\",\"name\":\"about\",\"type\":\"address\"},{\"indexed\":true,\"internalType\":\"bytes32\",\"name\":\"key\",\"type\":\"bytes32\"},{\"indexed\":false,\"internalType\":\"bytes\",\"name\":\"val\",\"type\":\"bytes\"}],\"name\":\"AttestationCreated\",\"type\":\"event\"},{\"inputs\":[{\"components\":[{\"internalType\":\"address\",\"name\":\"about\",\"type\":\"address\"},{\"internalType\":\"bytes32\",\"name\":\"key\",\"type\":\"bytes32\"},{\"internalType\":\"bytes\",\"name\":\"val\",\"type\":\"bytes\"}],\"internalType\":\"struct AttestationStation.AttestationData[]\",\"name\":\"_attestations\",\"type\":\"tuple[]\"}],\"name\":\"attest\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"_about\",\"type\":\"address\"},{\"internalType\":\"bytes32\",\"name\":\"_key\",\"type\":\"bytes32\"},{\"internalType\":\"bytes\",\"name\":\"_val\",\"type\":\"bytes\"}],\"name\":\"attest\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"},{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"},{\"internalType\":\"bytes32\",\"name\":\"\",\"type\":\"bytes32\"}],\"name\":\"attestations\",\"outputs\":[{\"internalType\":\"bytes\",\"name\":\"\",\"type\":\"bytes\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"version\",\"outputs\":[{\"internalType\":\"string\",\"name\":\"\",\"type\":\"string\"}],\"stateMutability\":\"view\",\"type\":\"function\"}],\"devdoc\":{\"author\":\"Optimism CollectiveGitcoin\",\"events\":{\"AttestationCreated(address,address,bytes32,bytes)\":{\"params\":{\"about\":\"Address attestation is about.\",\"creator\":\"Address that made the attestation.\",\"key\":\"Key of the attestation.\",\"val\":\"Value of the attestation.\"}}},\"kind\":\"dev\",\"methods\":{\"attest((address,bytes32,bytes)[])\":{\"params\":{\"_attestations\":\"An array of attestation data.\"}},\"attest(address,bytes32,bytes)\":{\"params\":{\"_about\":\"Address that the attestation is about.\",\"_key\":\"A key used to namespace the attestation.\",\"_val\":\"An arbitrary value stored as part of the attestation.\"}},\"constructor\":{\"custom:semver\":\"1.1.0\"},\"version()\":{\"returns\":{\"_0\":\"Semver contract version as a string.\"}}},\"title\":\"AttestationStation\",\"version\":1},\"userdoc\":{\"events\":{\"AttestationCreated(address,address,bytes32,bytes)\":{\"notice\":\"Emitted when Attestation is created.\"}},\"kind\":\"user\",\"methods\":{\"attest((address,bytes32,bytes)[])\":{\"notice\":\"Allows anyone to create attestations.\"},\"attest(address,bytes32,bytes)\":{\"notice\":\"Allows anyone to create an attestation.\"},\"attestations(address,address,bytes32)\":{\"notice\":\"Maps addresses to attestations. Creator => About => Key => Value.\"},\"version()\":{\"notice\":\"Returns the full semver contract version.\"}},\"notice\":\"Where attestations live.\",\"version\":1}},\"settings\":{\"compilationTarget\":{\"contracts/universal/op-nft/AttestationStation.sol\":\"AttestationStation\"},\"evmVersion\":\"london\",\"libraries\":{},\"metadata\":{\"bytecodeHash\":\"ipfs\",\"useLiteralContent\":true},\"optimizer\":{\"enabled\":true,\"runs\":10000},\"remappings\":[]},\"sources\":{\"@eth-optimism/contracts-bedrock/contracts/universal/Semver.sol\":{\"content\":\"// SPDX-License-Identifier: MIT\\npragma solidity ^0.8.15;\\n\\nimport { Strings } from \\\"@openzeppelin/contracts/utils/Strings.sol\\\";\\n\\n/**\\n * @title Semver\\n * @notice Semver is a simple contract for managing contract versions.\\n */\\ncontract Semver {\\n /**\\n * @notice Contract version number (major).\\n */\\n uint256 private immutable MAJOR_VERSION;\\n\\n /**\\n * @notice Contract version number (minor).\\n */\\n uint256 private immutable MINOR_VERSION;\\n\\n /**\\n * @notice Contract version number (patch).\\n */\\n uint256 private immutable PATCH_VERSION;\\n\\n /**\\n * @param _major Version number (major).\\n * @param _minor Version number (minor).\\n * @param _patch Version number (patch).\\n */\\n constructor(\\n uint256 _major,\\n uint256 _minor,\\n uint256 _patch\\n ) {\\n MAJOR_VERSION = _major;\\n MINOR_VERSION = _minor;\\n PATCH_VERSION = _patch;\\n }\\n\\n /**\\n * @notice Returns the full semver contract version.\\n *\\n * @return Semver contract version as a string.\\n */\\n function version() public view returns (string memory) {\\n return\\n string(\\n abi.encodePacked(\\n Strings.toString(MAJOR_VERSION),\\n \\\".\\\",\\n Strings.toString(MINOR_VERSION),\\n \\\".\\\",\\n Strings.toString(PATCH_VERSION)\\n )\\n );\\n }\\n}\\n\",\"keccak256\":\"0x979b13465de4996a1105850abbf48abe7f71d5e18a8d4af318597ee14c165fdf\",\"license\":\"MIT\"},\"@openzeppelin/contracts/utils/Strings.sol\":{\"content\":\"// SPDX-License-Identifier: MIT\\n// OpenZeppelin Contracts (last updated v4.7.0) (utils/Strings.sol)\\n\\npragma solidity ^0.8.0;\\n\\n/**\\n * @dev String operations.\\n */\\nlibrary Strings {\\n bytes16 private constant _HEX_SYMBOLS = \\\"0123456789abcdef\\\";\\n uint8 private constant _ADDRESS_LENGTH = 20;\\n\\n /**\\n * @dev Converts a `uint256` to its ASCII `string` decimal representation.\\n */\\n function toString(uint256 value) internal pure returns (string memory) {\\n // Inspired by OraclizeAPI's implementation - MIT licence\\n // https://github.com/oraclize/ethereum-api/blob/b42146b063c7d6ee1358846c198246239e9360e8/oraclizeAPI_0.4.25.sol\\n\\n if (value == 0) {\\n return \\\"0\\\";\\n }\\n uint256 temp = value;\\n uint256 digits;\\n while (temp != 0) {\\n digits++;\\n temp /= 10;\\n }\\n bytes memory buffer = new bytes(digits);\\n while (value != 0) {\\n digits -= 1;\\n buffer[digits] = bytes1(uint8(48 + uint256(value % 10)));\\n value /= 10;\\n }\\n return string(buffer);\\n }\\n\\n /**\\n * @dev Converts a `uint256` to its ASCII `string` hexadecimal representation.\\n */\\n function toHexString(uint256 value) internal pure returns (string memory) {\\n if (value == 0) {\\n return \\\"0x00\\\";\\n }\\n uint256 temp = value;\\n uint256 length = 0;\\n while (temp != 0) {\\n length++;\\n temp >>= 8;\\n }\\n return toHexString(value, length);\\n }\\n\\n /**\\n * @dev Converts a `uint256` to its ASCII `string` hexadecimal representation with fixed length.\\n */\\n function toHexString(uint256 value, uint256 length) internal pure returns (string memory) {\\n bytes memory buffer = new bytes(2 * length + 2);\\n buffer[0] = \\\"0\\\";\\n buffer[1] = \\\"x\\\";\\n for (uint256 i = 2 * length + 1; i > 1; --i) {\\n buffer[i] = _HEX_SYMBOLS[value & 0xf];\\n value >>= 4;\\n }\\n require(value == 0, \\\"Strings: hex length insufficient\\\");\\n return string(buffer);\\n }\\n\\n /**\\n * @dev Converts an `address` with fixed length of 20 bytes to its not checksummed ASCII `string` hexadecimal representation.\\n */\\n function toHexString(address addr) internal pure returns (string memory) {\\n return toHexString(uint256(uint160(addr)), _ADDRESS_LENGTH);\\n }\\n}\\n\",\"keccak256\":\"0xaf159a8b1923ad2a26d516089bceca9bdeaeacd04be50983ea00ba63070f08a3\",\"license\":\"MIT\"},\"contracts/universal/op-nft/AttestationStation.sol\":{\"content\":\"// SPDX-License-Identifier: MIT\\npragma solidity 0.8.15;\\n\\nimport { Semver } from \\\"@eth-optimism/contracts-bedrock/contracts/universal/Semver.sol\\\";\\n\\n/**\\n * @title AttestationStation\\n * @author Optimism Collective\\n * @author Gitcoin\\n * @notice Where attestations live.\\n */\\ncontract AttestationStation is Semver {\\n /**\\n * @notice Struct representing data that is being attested.\\n *\\n * @custom:field about Address for which the attestation is about.\\n * @custom:field key A bytes32 key for the attestation.\\n * @custom:field val The attestation as arbitrary bytes.\\n */\\n struct AttestationData {\\n address about;\\n bytes32 key;\\n bytes val;\\n }\\n\\n /**\\n * @notice Maps addresses to attestations. Creator => About => Key => Value.\\n */\\n mapping(address => mapping(address => mapping(bytes32 => bytes))) public attestations;\\n\\n /**\\n * @notice Emitted when Attestation is created.\\n *\\n * @param creator Address that made the attestation.\\n * @param about Address attestation is about.\\n * @param key Key of the attestation.\\n * @param val Value of the attestation.\\n */\\n event AttestationCreated(\\n address indexed creator,\\n address indexed about,\\n bytes32 indexed key,\\n bytes val\\n );\\n\\n /**\\n * @custom:semver 1.1.0\\n */\\n constructor() Semver(1, 1, 0) {}\\n\\n /**\\n * @notice Allows anyone to create an attestation.\\n *\\n * @param _about Address that the attestation is about.\\n * @param _key A key used to namespace the attestation.\\n * @param _val An arbitrary value stored as part of the attestation.\\n */\\n function attest(\\n address _about,\\n bytes32 _key,\\n bytes memory _val\\n ) public {\\n attestations[msg.sender][_about][_key] = _val;\\n\\n emit AttestationCreated(msg.sender, _about, _key, _val);\\n }\\n\\n /**\\n * @notice Allows anyone to create attestations.\\n *\\n * @param _attestations An array of attestation data.\\n */\\n function attest(AttestationData[] calldata _attestations) external {\\n uint256 length = _attestations.length;\\n for (uint256 i = 0; i < length; ) {\\n AttestationData memory attestation = _attestations[i];\\n\\n attest(attestation.about, attestation.key, attestation.val);\\n\\n unchecked {\\n ++i;\\n }\\n }\\n }\\n}\\n\",\"keccak256\":\"0x80718fdc09061ea8f8b99a0b846eee46c5a2a1372dbb0cbc3d40953cb3af19e0\",\"license\":\"MIT\"}},\"version\":1}",
"bytecode": "0x60e060405234801561001057600080fd5b506001608081905260a052600060c05260805160a05160c051610b1c61004f60003960006101ad015260006101840152600061015b0152610b1c6000f3fe608060405234801561001057600080fd5b506004361061004c5760003560e01c806329b42cb51461005157806354fd4d501461007a5780635eb5ea1014610082578063702b9dee14610097575b600080fd5b61006461005f36600461046c565b6100aa565b604051610071919061051e565b60405180910390f35b610064610154565b610095610090366004610538565b6101f7565b005b6100956100a5366004610687565b61025a565b60006020818152938152604080822085529281528281209093528252902080546100d3906106de565b80601f01602080910402602001604051908101604052809291908181526020018280546100ff906106de565b801561014c5780601f106101215761010080835404028352916020019161014c565b820191906000526020600020905b81548152906001019060200180831161012f57829003601f168201915b505050505081565b606061017f7f0000000000000000000000000000000000000000000000000000000000000000610306565b6101a87f0000000000000000000000000000000000000000000000000000000000000000610306565b6101d17f0000000000000000000000000000000000000000000000000000000000000000610306565b6040516020016101e393929190610731565b604051602081830303815290604052905090565b8060005b81811015610254576000848483818110610217576102176107a7565b905060200281019061022991906107d6565b61023290610814565b905061024b81600001518260200151836040015161025a565b506001016101fb565b50505050565b3360009081526020818152604080832073ffffffffffffffffffffffffffffffffffffffff871684528252808320858452909152902061029a82826108df565b50818373ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff167f28710dfecab43d1e29e02aa56b2e1e610c0bae19135c9cf7a83a1adb6df96d85846040516102f9919061051e565b60405180910390a4505050565b60608160000361034957505060408051808201909152600181527f3000000000000000000000000000000000000000000000000000000000000000602082015290565b8160005b8115610373578061035d81610a28565b915061036c9050600a83610a8f565b915061034d565b60008167ffffffffffffffff81111561038e5761038e6105ad565b6040519080825280601f01601f1916602001820160405280156103b8576020820181803683370190505b5090505b841561043b576103cd600183610aa3565b91506103da600a86610aba565b6103e5906030610ace565b60f81b8183815181106103fa576103fa6107a7565b60200101907effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff1916908160001a905350610434600a86610a8f565b94506103bc565b949350505050565b803573ffffffffffffffffffffffffffffffffffffffff8116811461046757600080fd5b919050565b60008060006060848603121561048157600080fd5b61048a84610443565b925061049860208501610443565b9150604084013590509250925092565b60005b838110156104c35781810151838201526020016104ab565b838111156102545750506000910152565b600081518084526104ec8160208601602086016104a8565b601f017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0169290920160200192915050565b60208152600061053160208301846104d4565b9392505050565b6000806020838503121561054b57600080fd5b823567ffffffffffffffff8082111561056357600080fd5b818501915085601f83011261057757600080fd5b81358181111561058657600080fd5b8660208260051b850101111561059b57600080fd5b60209290920196919550909350505050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b600082601f8301126105ed57600080fd5b813567ffffffffffffffff80821115610608576106086105ad565b604051601f83017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0908116603f0116810190828211818310171561064e5761064e6105ad565b8160405283815286602085880101111561066757600080fd5b836020870160208301376000602085830101528094505050505092915050565b60008060006060848603121561069c57600080fd5b6106a584610443565b925060208401359150604084013567ffffffffffffffff8111156106c857600080fd5b6106d4868287016105dc565b9150509250925092565b600181811c908216806106f257607f821691505b60208210810361072b577f4e487b7100000000000000000000000000000000000000000000000000000000600052602260045260246000fd5b50919050565b600084516107438184602089016104a8565b80830190507f2e00000000000000000000000000000000000000000000000000000000000000808252855161077f816001850160208a016104a8565b6001920191820152835161079a8160028401602088016104a8565b0160020195945050505050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052603260045260246000fd5b600082357fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffa183360301811261080a57600080fd5b9190910192915050565b60006060823603121561082657600080fd5b6040516060810167ffffffffffffffff828210818311171561084a5761084a6105ad565b8160405261085785610443565b835260208501356020840152604085013591508082111561087757600080fd5b50610884368286016105dc565b60408301525092915050565b601f8211156108da57600081815260208120601f850160051c810160208610156108b75750805b601f850160051c820191505b818110156108d6578281556001016108c3565b5050505b505050565b815167ffffffffffffffff8111156108f9576108f96105ad565b61090d8161090784546106de565b84610890565b602080601f831160018114610960576000841561092a5750858301515b7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff600386901b1c1916600185901b1785556108d6565b6000858152602081207fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe08616915b828110156109ad5788860151825594840194600190910190840161098e565b50858210156109e957878501517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff600388901b60f8161c191681555b5050505050600190811b01905550565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601160045260246000fd5b60007fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff8203610a5957610a596109f9565b5060010190565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601260045260246000fd5b600082610a9e57610a9e610a60565b500490565b600082821015610ab557610ab56109f9565b500390565b600082610ac957610ac9610a60565b500690565b60008219821115610ae157610ae16109f9565b50019056fea2646970667358221220bfd4abacc1c2510682b093eea8745255cdd9f45c5b16c168540522a65ddc1f3264736f6c634300080f0033",
"deployedBytecode": "0x608060405234801561001057600080fd5b506004361061004c5760003560e01c806329b42cb51461005157806354fd4d501461007a5780635eb5ea1014610082578063702b9dee14610097575b600080fd5b61006461005f36600461046c565b6100aa565b604051610071919061051e565b60405180910390f35b610064610154565b610095610090366004610538565b6101f7565b005b6100956100a5366004610687565b61025a565b60006020818152938152604080822085529281528281209093528252902080546100d3906106de565b80601f01602080910402602001604051908101604052809291908181526020018280546100ff906106de565b801561014c5780601f106101215761010080835404028352916020019161014c565b820191906000526020600020905b81548152906001019060200180831161012f57829003601f168201915b505050505081565b606061017f7f0000000000000000000000000000000000000000000000000000000000000000610306565b6101a87f0000000000000000000000000000000000000000000000000000000000000000610306565b6101d17f0000000000000000000000000000000000000000000000000000000000000000610306565b6040516020016101e393929190610731565b604051602081830303815290604052905090565b8060005b81811015610254576000848483818110610217576102176107a7565b905060200281019061022991906107d6565b61023290610814565b905061024b81600001518260200151836040015161025a565b506001016101fb565b50505050565b3360009081526020818152604080832073ffffffffffffffffffffffffffffffffffffffff871684528252808320858452909152902061029a82826108df565b50818373ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff167f28710dfecab43d1e29e02aa56b2e1e610c0bae19135c9cf7a83a1adb6df96d85846040516102f9919061051e565b60405180910390a4505050565b60608160000361034957505060408051808201909152600181527f3000000000000000000000000000000000000000000000000000000000000000602082015290565b8160005b8115610373578061035d81610a28565b915061036c9050600a83610a8f565b915061034d565b60008167ffffffffffffffff81111561038e5761038e6105ad565b6040519080825280601f01601f1916602001820160405280156103b8576020820181803683370190505b5090505b841561043b576103cd600183610aa3565b91506103da600a86610aba565b6103e5906030610ace565b60f81b8183815181106103fa576103fa6107a7565b60200101907effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff1916908160001a905350610434600a86610a8f565b94506103bc565b949350505050565b803573ffffffffffffffffffffffffffffffffffffffff8116811461046757600080fd5b919050565b60008060006060848603121561048157600080fd5b61048a84610443565b925061049860208501610443565b9150604084013590509250925092565b60005b838110156104c35781810151838201526020016104ab565b838111156102545750506000910152565b600081518084526104ec8160208601602086016104a8565b601f017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0169290920160200192915050565b60208152600061053160208301846104d4565b9392505050565b6000806020838503121561054b57600080fd5b823567ffffffffffffffff8082111561056357600080fd5b818501915085601f83011261057757600080fd5b81358181111561058657600080fd5b8660208260051b850101111561059b57600080fd5b60209290920196919550909350505050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b600082601f8301126105ed57600080fd5b813567ffffffffffffffff80821115610608576106086105ad565b604051601f83017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0908116603f0116810190828211818310171561064e5761064e6105ad565b8160405283815286602085880101111561066757600080fd5b836020870160208301376000602085830101528094505050505092915050565b60008060006060848603121561069c57600080fd5b6106a584610443565b925060208401359150604084013567ffffffffffffffff8111156106c857600080fd5b6106d4868287016105dc565b9150509250925092565b600181811c908216806106f257607f821691505b60208210810361072b577f4e487b7100000000000000000000000000000000000000000000000000000000600052602260045260246000fd5b50919050565b600084516107438184602089016104a8565b80830190507f2e00000000000000000000000000000000000000000000000000000000000000808252855161077f816001850160208a016104a8565b6001920191820152835161079a8160028401602088016104a8565b0160020195945050505050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052603260045260246000fd5b600082357fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffa183360301811261080a57600080fd5b9190910192915050565b60006060823603121561082657600080fd5b6040516060810167ffffffffffffffff828210818311171561084a5761084a6105ad565b8160405261085785610443565b835260208501356020840152604085013591508082111561087757600080fd5b50610884368286016105dc565b60408301525092915050565b601f8211156108da57600081815260208120601f850160051c810160208610156108b75750805b601f850160051c820191505b818110156108d6578281556001016108c3565b5050505b505050565b815167ffffffffffffffff8111156108f9576108f96105ad565b61090d8161090784546106de565b84610890565b602080601f831160018114610960576000841561092a5750858301515b7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff600386901b1c1916600185901b1785556108d6565b6000858152602081207fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe08616915b828110156109ad5788860151825594840194600190910190840161098e565b50858210156109e957878501517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff600388901b60f8161c191681555b5050505050600190811b01905550565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601160045260246000fd5b60007fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff8203610a5957610a596109f9565b5060010190565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601260045260246000fd5b600082610a9e57610a9e610a60565b500490565b600082821015610ab557610ab56109f9565b500390565b600082610ac957610ac9610a60565b500690565b60008219821115610ae157610ae16109f9565b50019056fea2646970667358221220bfd4abacc1c2510682b093eea8745255cdd9f45c5b16c168540522a65ddc1f3264736f6c634300080f0033",
"devdoc": {
"author": "Optimism CollectiveGitcoin",
"events": {
......@@ -151,8 +174,15 @@
"_attestations": "An array of attestation data."
}
},
"attest(address,bytes32,bytes)": {
"params": {
"_about": "Address that the attestation is about.",
"_key": "A key used to namespace the attestation.",
"_val": "An arbitrary value stored as part of the attestation."
}
},
"constructor": {
"custom:semver": "1.0.0"
"custom:semver": "1.1.0"
},
"version()": {
"returns": {
......@@ -174,6 +204,9 @@
"attest((address,bytes32,bytes)[])": {
"notice": "Allows anyone to create attestations."
},
"attest(address,bytes32,bytes)": {
"notice": "Allows anyone to create an attestation."
},
"attestations(address,address,bytes32)": {
"notice": "Maps addresses to attestations. Creator => About => Key => Value."
},
......@@ -187,7 +220,7 @@
"storageLayout": {
"storage": [
{
"astId": 8191,
"astId": 2135,
"contract": "contracts/universal/op-nft/AttestationStation.sol:AttestationStation",
"label": "attestations",
"offset": 0,
......
{
"language": "Solidity",
"sources": {
"contracts/universal/op-nft/AttestationStation.sol": {
"content": "// SPDX-License-Identifier: MIT\npragma solidity 0.8.15;\n\nimport { Semver } from \"@eth-optimism/contracts-bedrock/contracts/universal/Semver.sol\";\n\n/**\n * @title AttestationStation\n * @author Optimism Collective\n * @author Gitcoin\n * @notice Where attestations live.\n */\ncontract AttestationStation is Semver {\n /**\n * @notice Struct representing data that is being attested.\n *\n * @custom:field about Address for which the attestation is about.\n * @custom:field key A bytes32 key for the attestation.\n * @custom:field val The attestation as arbitrary bytes.\n */\n struct AttestationData {\n address about;\n bytes32 key;\n bytes val;\n }\n\n /**\n * @notice Maps addresses to attestations. Creator => About => Key => Value.\n */\n mapping(address => mapping(address => mapping(bytes32 => bytes))) public attestations;\n\n /**\n * @notice Emitted when Attestation is created.\n *\n * @param creator Address that made the attestation.\n * @param about Address attestation is about.\n * @param key Key of the attestation.\n * @param val Value of the attestation.\n */\n event AttestationCreated(\n address indexed creator,\n address indexed about,\n bytes32 indexed key,\n bytes val\n );\n\n /**\n * @custom:semver 1.1.0\n */\n constructor() Semver(1, 1, 0) {}\n\n /**\n * @notice Allows anyone to create an attestation.\n *\n * @param _about Address that the attestation is about.\n * @param _key A key used to namespace the attestation.\n * @param _val An arbitrary value stored as part of the attestation.\n */\n function attest(\n address _about,\n bytes32 _key,\n bytes memory _val\n ) public {\n attestations[msg.sender][_about][_key] = _val;\n\n emit AttestationCreated(msg.sender, _about, _key, _val);\n }\n\n /**\n * @notice Allows anyone to create attestations.\n *\n * @param _attestations An array of attestation data.\n */\n function attest(AttestationData[] calldata _attestations) external {\n uint256 length = _attestations.length;\n for (uint256 i = 0; i < length; ) {\n AttestationData memory attestation = _attestations[i];\n\n attest(attestation.about, attestation.key, attestation.val);\n\n unchecked {\n ++i;\n }\n }\n }\n}\n"
},
"@eth-optimism/contracts-bedrock/contracts/universal/Semver.sol": {
"content": "// SPDX-License-Identifier: MIT\npragma solidity ^0.8.15;\n\nimport { Strings } from \"@openzeppelin/contracts/utils/Strings.sol\";\n\n/**\n * @title Semver\n * @notice Semver is a simple contract for managing contract versions.\n */\ncontract Semver {\n /**\n * @notice Contract version number (major).\n */\n uint256 private immutable MAJOR_VERSION;\n\n /**\n * @notice Contract version number (minor).\n */\n uint256 private immutable MINOR_VERSION;\n\n /**\n * @notice Contract version number (patch).\n */\n uint256 private immutable PATCH_VERSION;\n\n /**\n * @param _major Version number (major).\n * @param _minor Version number (minor).\n * @param _patch Version number (patch).\n */\n constructor(\n uint256 _major,\n uint256 _minor,\n uint256 _patch\n ) {\n MAJOR_VERSION = _major;\n MINOR_VERSION = _minor;\n PATCH_VERSION = _patch;\n }\n\n /**\n * @notice Returns the full semver contract version.\n *\n * @return Semver contract version as a string.\n */\n function version() public view returns (string memory) {\n return\n string(\n abi.encodePacked(\n Strings.toString(MAJOR_VERSION),\n \".\",\n Strings.toString(MINOR_VERSION),\n \".\",\n Strings.toString(PATCH_VERSION)\n )\n );\n }\n}\n"
},
"@openzeppelin/contracts/utils/Strings.sol": {
"content": "// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts (last updated v4.7.0) (utils/Strings.sol)\n\npragma solidity ^0.8.0;\n\n/**\n * @dev String operations.\n */\nlibrary Strings {\n bytes16 private constant _HEX_SYMBOLS = \"0123456789abcdef\";\n uint8 private constant _ADDRESS_LENGTH = 20;\n\n /**\n * @dev Converts a `uint256` to its ASCII `string` decimal representation.\n */\n function toString(uint256 value) internal pure returns (string memory) {\n // Inspired by OraclizeAPI's implementation - MIT licence\n // https://github.com/oraclize/ethereum-api/blob/b42146b063c7d6ee1358846c198246239e9360e8/oraclizeAPI_0.4.25.sol\n\n if (value == 0) {\n return \"0\";\n }\n uint256 temp = value;\n uint256 digits;\n while (temp != 0) {\n digits++;\n temp /= 10;\n }\n bytes memory buffer = new bytes(digits);\n while (value != 0) {\n digits -= 1;\n buffer[digits] = bytes1(uint8(48 + uint256(value % 10)));\n value /= 10;\n }\n return string(buffer);\n }\n\n /**\n * @dev Converts a `uint256` to its ASCII `string` hexadecimal representation.\n */\n function toHexString(uint256 value) internal pure returns (string memory) {\n if (value == 0) {\n return \"0x00\";\n }\n uint256 temp = value;\n uint256 length = 0;\n while (temp != 0) {\n length++;\n temp >>= 8;\n }\n return toHexString(value, length);\n }\n\n /**\n * @dev Converts a `uint256` to its ASCII `string` hexadecimal representation with fixed length.\n */\n function toHexString(uint256 value, uint256 length) internal pure returns (string memory) {\n bytes memory buffer = new bytes(2 * length + 2);\n buffer[0] = \"0\";\n buffer[1] = \"x\";\n for (uint256 i = 2 * length + 1; i > 1; --i) {\n buffer[i] = _HEX_SYMBOLS[value & 0xf];\n value >>= 4;\n }\n require(value == 0, \"Strings: hex length insufficient\");\n return string(buffer);\n }\n\n /**\n * @dev Converts an `address` with fixed length of 20 bytes to its not checksummed ASCII `string` hexadecimal representation.\n */\n function toHexString(address addr) internal pure returns (string memory) {\n return toHexString(uint256(uint160(addr)), _ADDRESS_LENGTH);\n }\n}\n"
},
"contracts/universal/op-nft/Optimist.sol": {
"content": "// SPDX-License-Identifier: MIT\npragma solidity 0.8.15;\n\nimport { Semver } from \"@eth-optimism/contracts-bedrock/contracts/universal/Semver.sol\";\nimport {\n ERC721BurnableUpgradeable\n} from \"@openzeppelin/contracts-upgradeable/token/ERC721/extensions/ERC721BurnableUpgradeable.sol\";\nimport { AttestationStation } from \"./AttestationStation.sol\";\nimport { Strings } from \"@openzeppelin/contracts/utils/Strings.sol\";\n\n/**\n * @author Optimism Collective\n * @author Gitcoin\n * @title Optimist\n * @notice A Soul Bound Token for real humans only(tm).\n */\ncontract Optimist is ERC721BurnableUpgradeable, Semver {\n /**\n * @notice Address of the AttestationStation contract.\n */\n AttestationStation public immutable ATTESTATION_STATION;\n\n /**\n * @notice Attestor who attests to baseURI and allowlist.\n */\n address public immutable ATTESTOR;\n\n /**\n * @custom:semver 1.0.0\n * @param _name Token name.\n * @param _symbol Token symbol.\n * @param _attestor Address of the attestor.\n * @param _attestationStation Address of the AttestationStation contract.\n */\n constructor(\n string memory _name,\n string memory _symbol,\n address _attestor,\n AttestationStation _attestationStation\n ) Semver(1, 0, 0) {\n ATTESTOR = _attestor;\n ATTESTATION_STATION = _attestationStation;\n initialize(_name, _symbol);\n }\n\n /**\n * @notice Initializes the Optimist contract.\n *\n * @param _name Token name.\n * @param _symbol Token symbol.\n */\n function initialize(string memory _name, string memory _symbol) public initializer {\n __ERC721_init(_name, _symbol);\n __ERC721Burnable_init();\n }\n\n /**\n * @notice Allows an address to mint an Optimist NFT. Token ID is the uint256 representation\n * of the recipient's address. Recipients must be permitted to mint, eventually anyone\n * will be able to mint. One token per address.\n *\n * @param _recipient Address of the token recipient.\n */\n function mint(address _recipient) public {\n require(isOnAllowList(_recipient), \"Optimist: address is not on allowList\");\n _safeMint(_recipient, tokenIdOfAddress(_recipient));\n }\n\n /**\n * @notice Returns the baseURI for all tokens.\n *\n * @return BaseURI for all tokens.\n */\n function baseURI() public view returns (string memory) {\n return\n string(\n abi.encodePacked(\n ATTESTATION_STATION.attestations(\n ATTESTOR,\n address(this),\n bytes32(\"optimist.base-uri\")\n )\n )\n );\n }\n\n /**\n * @notice Returns the token URI for a given token by ID\n *\n * @param _tokenId Token ID to query.\n\n * @return Token URI for the given token by ID.\n */\n function tokenURI(uint256 _tokenId) public view virtual override returns (string memory) {\n return\n string(\n abi.encodePacked(\n baseURI(),\n \"/\",\n // Properly format the token ID as a 20 byte hex string (address).\n Strings.toHexString(_tokenId, 20),\n \".json\"\n )\n );\n }\n\n /**\n * @notice Checks whether a given address is allowed to mint the Optimist NFT yet. Since the\n * Optimist NFT will also be used as part of the Citizens House, mints are currently\n * restricted. Eventually anyone will be able to mint.\n *\n * @return Whether or not the address is allowed to mint yet.\n */\n function isOnAllowList(address _recipient) public view returns (bool) {\n return\n ATTESTATION_STATION\n .attestations(ATTESTOR, _recipient, bytes32(\"optimist.can-mint\"))\n .length > 0;\n }\n\n /**\n * @notice Returns the token ID for the token owned by a given address. This is the uint256\n * representation of the given address.\n *\n * @return Token ID for the token owned by the given address.\n */\n function tokenIdOfAddress(address _owner) public pure returns (uint256) {\n return uint256(uint160(_owner));\n }\n\n /**\n * @notice Disabled for the Optimist NFT (Soul Bound Token).\n */\n function approve(address, uint256) public pure override {\n revert(\"Optimist: soul bound token\");\n }\n\n /**\n * @notice Disabled for the Optimist NFT (Soul Bound Token).\n */\n function setApprovalForAll(address, bool) public virtual override {\n revert(\"Optimist: soul bound token\");\n }\n\n /**\n * @notice Prevents transfers of the Optimist NFT (Soul Bound Token).\n *\n * @param _from Address of the token sender.\n * @param _to Address of the token recipient.\n */\n function _beforeTokenTransfer(\n address _from,\n address _to,\n uint256\n ) internal virtual override {\n require(_from == address(0) || _to == address(0), \"Optimist: soul bound token\");\n }\n}\n"
},
"@openzeppelin/contracts-upgradeable/token/ERC721/extensions/ERC721BurnableUpgradeable.sol": {
"content": "// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts (last updated v4.7.0) (token/ERC721/extensions/ERC721Burnable.sol)\n\npragma solidity ^0.8.0;\n\nimport \"../ERC721Upgradeable.sol\";\nimport \"../../../utils/ContextUpgradeable.sol\";\nimport \"../../../proxy/utils/Initializable.sol\";\n\n/**\n * @title ERC721 Burnable Token\n * @dev ERC721 Token that can be burned (destroyed).\n */\nabstract contract ERC721BurnableUpgradeable is Initializable, ContextUpgradeable, ERC721Upgradeable {\n function __ERC721Burnable_init() internal onlyInitializing {\n }\n\n function __ERC721Burnable_init_unchained() internal onlyInitializing {\n }\n /**\n * @dev Burns `tokenId`. See {ERC721-_burn}.\n *\n * Requirements:\n *\n * - The caller must own `tokenId` or be an approved operator.\n */\n function burn(uint256 tokenId) public virtual {\n //solhint-disable-next-line max-line-length\n require(_isApprovedOrOwner(_msgSender(), tokenId), \"ERC721: caller is not token owner nor approved\");\n _burn(tokenId);\n }\n\n /**\n * @dev This empty reserved space is put in place to allow future versions to add new\n * variables without shifting down storage in the inheritance chain.\n * See https://docs.openzeppelin.com/contracts/4.x/upgradeable#storage_gaps\n */\n uint256[50] private __gap;\n}\n"
},
"@openzeppelin/contracts-upgradeable/token/ERC721/ERC721Upgradeable.sol": {
"content": "// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts (last updated v4.7.0) (token/ERC721/ERC721.sol)\n\npragma solidity ^0.8.0;\n\nimport \"./IERC721Upgradeable.sol\";\nimport \"./IERC721ReceiverUpgradeable.sol\";\nimport \"./extensions/IERC721MetadataUpgradeable.sol\";\nimport \"../../utils/AddressUpgradeable.sol\";\nimport \"../../utils/ContextUpgradeable.sol\";\nimport \"../../utils/StringsUpgradeable.sol\";\nimport \"../../utils/introspection/ERC165Upgradeable.sol\";\nimport \"../../proxy/utils/Initializable.sol\";\n\n/**\n * @dev Implementation of https://eips.ethereum.org/EIPS/eip-721[ERC721] Non-Fungible Token Standard, including\n * the Metadata extension, but not including the Enumerable extension, which is available separately as\n * {ERC721Enumerable}.\n */\ncontract ERC721Upgradeable is Initializable, ContextUpgradeable, ERC165Upgradeable, IERC721Upgradeable, IERC721MetadataUpgradeable {\n using AddressUpgradeable for address;\n using StringsUpgradeable for uint256;\n\n // Token name\n string private _name;\n\n // Token symbol\n string private _symbol;\n\n // Mapping from token ID to owner address\n mapping(uint256 => address) private _owners;\n\n // Mapping owner address to token count\n mapping(address => uint256) private _balances;\n\n // Mapping from token ID to approved address\n mapping(uint256 => address) private _tokenApprovals;\n\n // Mapping from owner to operator approvals\n mapping(address => mapping(address => bool)) private _operatorApprovals;\n\n /**\n * @dev Initializes the contract by setting a `name` and a `symbol` to the token collection.\n */\n function __ERC721_init(string memory name_, string memory symbol_) internal onlyInitializing {\n __ERC721_init_unchained(name_, symbol_);\n }\n\n function __ERC721_init_unchained(string memory name_, string memory symbol_) internal onlyInitializing {\n _name = name_;\n _symbol = symbol_;\n }\n\n /**\n * @dev See {IERC165-supportsInterface}.\n */\n function supportsInterface(bytes4 interfaceId) public view virtual override(ERC165Upgradeable, IERC165Upgradeable) returns (bool) {\n return\n interfaceId == type(IERC721Upgradeable).interfaceId ||\n interfaceId == type(IERC721MetadataUpgradeable).interfaceId ||\n super.supportsInterface(interfaceId);\n }\n\n /**\n * @dev See {IERC721-balanceOf}.\n */\n function balanceOf(address owner) public view virtual override returns (uint256) {\n require(owner != address(0), \"ERC721: address zero is not a valid owner\");\n return _balances[owner];\n }\n\n /**\n * @dev See {IERC721-ownerOf}.\n */\n function ownerOf(uint256 tokenId) public view virtual override returns (address) {\n address owner = _owners[tokenId];\n require(owner != address(0), \"ERC721: invalid token ID\");\n return owner;\n }\n\n /**\n * @dev See {IERC721Metadata-name}.\n */\n function name() public view virtual override returns (string memory) {\n return _name;\n }\n\n /**\n * @dev See {IERC721Metadata-symbol}.\n */\n function symbol() public view virtual override returns (string memory) {\n return _symbol;\n }\n\n /**\n * @dev See {IERC721Metadata-tokenURI}.\n */\n function tokenURI(uint256 tokenId) public view virtual override returns (string memory) {\n _requireMinted(tokenId);\n\n string memory baseURI = _baseURI();\n return bytes(baseURI).length > 0 ? string(abi.encodePacked(baseURI, tokenId.toString())) : \"\";\n }\n\n /**\n * @dev Base URI for computing {tokenURI}. If set, the resulting URI for each\n * token will be the concatenation of the `baseURI` and the `tokenId`. Empty\n * by default, can be overridden in child contracts.\n */\n function _baseURI() internal view virtual returns (string memory) {\n return \"\";\n }\n\n /**\n * @dev See {IERC721-approve}.\n */\n function approve(address to, uint256 tokenId) public virtual override {\n address owner = ERC721Upgradeable.ownerOf(tokenId);\n require(to != owner, \"ERC721: approval to current owner\");\n\n require(\n _msgSender() == owner || isApprovedForAll(owner, _msgSender()),\n \"ERC721: approve caller is not token owner nor approved for all\"\n );\n\n _approve(to, tokenId);\n }\n\n /**\n * @dev See {IERC721-getApproved}.\n */\n function getApproved(uint256 tokenId) public view virtual override returns (address) {\n _requireMinted(tokenId);\n\n return _tokenApprovals[tokenId];\n }\n\n /**\n * @dev See {IERC721-setApprovalForAll}.\n */\n function setApprovalForAll(address operator, bool approved) public virtual override {\n _setApprovalForAll(_msgSender(), operator, approved);\n }\n\n /**\n * @dev See {IERC721-isApprovedForAll}.\n */\n function isApprovedForAll(address owner, address operator) public view virtual override returns (bool) {\n return _operatorApprovals[owner][operator];\n }\n\n /**\n * @dev See {IERC721-transferFrom}.\n */\n function transferFrom(\n address from,\n address to,\n uint256 tokenId\n ) public virtual override {\n //solhint-disable-next-line max-line-length\n require(_isApprovedOrOwner(_msgSender(), tokenId), \"ERC721: caller is not token owner nor approved\");\n\n _transfer(from, to, tokenId);\n }\n\n /**\n * @dev See {IERC721-safeTransferFrom}.\n */\n function safeTransferFrom(\n address from,\n address to,\n uint256 tokenId\n ) public virtual override {\n safeTransferFrom(from, to, tokenId, \"\");\n }\n\n /**\n * @dev See {IERC721-safeTransferFrom}.\n */\n function safeTransferFrom(\n address from,\n address to,\n uint256 tokenId,\n bytes memory data\n ) public virtual override {\n require(_isApprovedOrOwner(_msgSender(), tokenId), \"ERC721: caller is not token owner nor approved\");\n _safeTransfer(from, to, tokenId, data);\n }\n\n /**\n * @dev Safely transfers `tokenId` token from `from` to `to`, checking first that contract recipients\n * are aware of the ERC721 protocol to prevent tokens from being forever locked.\n *\n * `data` is additional data, it has no specified format and it is sent in call to `to`.\n *\n * This internal function is equivalent to {safeTransferFrom}, and can be used to e.g.\n * implement alternative mechanisms to perform token transfer, such as signature-based.\n *\n * Requirements:\n *\n * - `from` cannot be the zero address.\n * - `to` cannot be the zero address.\n * - `tokenId` token must exist and be owned by `from`.\n * - If `to` refers to a smart contract, it must implement {IERC721Receiver-onERC721Received}, which is called upon a safe transfer.\n *\n * Emits a {Transfer} event.\n */\n function _safeTransfer(\n address from,\n address to,\n uint256 tokenId,\n bytes memory data\n ) internal virtual {\n _transfer(from, to, tokenId);\n require(_checkOnERC721Received(from, to, tokenId, data), \"ERC721: transfer to non ERC721Receiver implementer\");\n }\n\n /**\n * @dev Returns whether `tokenId` exists.\n *\n * Tokens can be managed by their owner or approved accounts via {approve} or {setApprovalForAll}.\n *\n * Tokens start existing when they are minted (`_mint`),\n * and stop existing when they are burned (`_burn`).\n */\n function _exists(uint256 tokenId) internal view virtual returns (bool) {\n return _owners[tokenId] != address(0);\n }\n\n /**\n * @dev Returns whether `spender` is allowed to manage `tokenId`.\n *\n * Requirements:\n *\n * - `tokenId` must exist.\n */\n function _isApprovedOrOwner(address spender, uint256 tokenId) internal view virtual returns (bool) {\n address owner = ERC721Upgradeable.ownerOf(tokenId);\n return (spender == owner || isApprovedForAll(owner, spender) || getApproved(tokenId) == spender);\n }\n\n /**\n * @dev Safely mints `tokenId` and transfers it to `to`.\n *\n * Requirements:\n *\n * - `tokenId` must not exist.\n * - If `to` refers to a smart contract, it must implement {IERC721Receiver-onERC721Received}, which is called upon a safe transfer.\n *\n * Emits a {Transfer} event.\n */\n function _safeMint(address to, uint256 tokenId) internal virtual {\n _safeMint(to, tokenId, \"\");\n }\n\n /**\n * @dev Same as {xref-ERC721-_safeMint-address-uint256-}[`_safeMint`], with an additional `data` parameter which is\n * forwarded in {IERC721Receiver-onERC721Received} to contract recipients.\n */\n function _safeMint(\n address to,\n uint256 tokenId,\n bytes memory data\n ) internal virtual {\n _mint(to, tokenId);\n require(\n _checkOnERC721Received(address(0), to, tokenId, data),\n \"ERC721: transfer to non ERC721Receiver implementer\"\n );\n }\n\n /**\n * @dev Mints `tokenId` and transfers it to `to`.\n *\n * WARNING: Usage of this method is discouraged, use {_safeMint} whenever possible\n *\n * Requirements:\n *\n * - `tokenId` must not exist.\n * - `to` cannot be the zero address.\n *\n * Emits a {Transfer} event.\n */\n function _mint(address to, uint256 tokenId) internal virtual {\n require(to != address(0), \"ERC721: mint to the zero address\");\n require(!_exists(tokenId), \"ERC721: token already minted\");\n\n _beforeTokenTransfer(address(0), to, tokenId);\n\n _balances[to] += 1;\n _owners[tokenId] = to;\n\n emit Transfer(address(0), to, tokenId);\n\n _afterTokenTransfer(address(0), to, tokenId);\n }\n\n /**\n * @dev Destroys `tokenId`.\n * The approval is cleared when the token is burned.\n *\n * Requirements:\n *\n * - `tokenId` must exist.\n *\n * Emits a {Transfer} event.\n */\n function _burn(uint256 tokenId) internal virtual {\n address owner = ERC721Upgradeable.ownerOf(tokenId);\n\n _beforeTokenTransfer(owner, address(0), tokenId);\n\n // Clear approvals\n _approve(address(0), tokenId);\n\n _balances[owner] -= 1;\n delete _owners[tokenId];\n\n emit Transfer(owner, address(0), tokenId);\n\n _afterTokenTransfer(owner, address(0), tokenId);\n }\n\n /**\n * @dev Transfers `tokenId` from `from` to `to`.\n * As opposed to {transferFrom}, this imposes no restrictions on msg.sender.\n *\n * Requirements:\n *\n * - `to` cannot be the zero address.\n * - `tokenId` token must be owned by `from`.\n *\n * Emits a {Transfer} event.\n */\n function _transfer(\n address from,\n address to,\n uint256 tokenId\n ) internal virtual {\n require(ERC721Upgradeable.ownerOf(tokenId) == from, \"ERC721: transfer from incorrect owner\");\n require(to != address(0), \"ERC721: transfer to the zero address\");\n\n _beforeTokenTransfer(from, to, tokenId);\n\n // Clear approvals from the previous owner\n _approve(address(0), tokenId);\n\n _balances[from] -= 1;\n _balances[to] += 1;\n _owners[tokenId] = to;\n\n emit Transfer(from, to, tokenId);\n\n _afterTokenTransfer(from, to, tokenId);\n }\n\n /**\n * @dev Approve `to` to operate on `tokenId`\n *\n * Emits an {Approval} event.\n */\n function _approve(address to, uint256 tokenId) internal virtual {\n _tokenApprovals[tokenId] = to;\n emit Approval(ERC721Upgradeable.ownerOf(tokenId), to, tokenId);\n }\n\n /**\n * @dev Approve `operator` to operate on all of `owner` tokens\n *\n * Emits an {ApprovalForAll} event.\n */\n function _setApprovalForAll(\n address owner,\n address operator,\n bool approved\n ) internal virtual {\n require(owner != operator, \"ERC721: approve to caller\");\n _operatorApprovals[owner][operator] = approved;\n emit ApprovalForAll(owner, operator, approved);\n }\n\n /**\n * @dev Reverts if the `tokenId` has not been minted yet.\n */\n function _requireMinted(uint256 tokenId) internal view virtual {\n require(_exists(tokenId), \"ERC721: invalid token ID\");\n }\n\n /**\n * @dev Internal function to invoke {IERC721Receiver-onERC721Received} on a target address.\n * The call is not executed if the target address is not a contract.\n *\n * @param from address representing the previous owner of the given token ID\n * @param to target address that will receive the tokens\n * @param tokenId uint256 ID of the token to be transferred\n * @param data bytes optional data to send along with the call\n * @return bool whether the call correctly returned the expected magic value\n */\n function _checkOnERC721Received(\n address from,\n address to,\n uint256 tokenId,\n bytes memory data\n ) private returns (bool) {\n if (to.isContract()) {\n try IERC721ReceiverUpgradeable(to).onERC721Received(_msgSender(), from, tokenId, data) returns (bytes4 retval) {\n return retval == IERC721ReceiverUpgradeable.onERC721Received.selector;\n } catch (bytes memory reason) {\n if (reason.length == 0) {\n revert(\"ERC721: transfer to non ERC721Receiver implementer\");\n } else {\n /// @solidity memory-safe-assembly\n assembly {\n revert(add(32, reason), mload(reason))\n }\n }\n }\n } else {\n return true;\n }\n }\n\n /**\n * @dev Hook that is called before any token transfer. This includes minting\n * and burning.\n *\n * Calling conditions:\n *\n * - When `from` and `to` are both non-zero, ``from``'s `tokenId` will be\n * transferred to `to`.\n * - When `from` is zero, `tokenId` will be minted for `to`.\n * - When `to` is zero, ``from``'s `tokenId` will be burned.\n * - `from` and `to` are never both zero.\n *\n * To learn more about hooks, head to xref:ROOT:extending-contracts.adoc#using-hooks[Using Hooks].\n */\n function _beforeTokenTransfer(\n address from,\n address to,\n uint256 tokenId\n ) internal virtual {}\n\n /**\n * @dev Hook that is called after any transfer of tokens. This includes\n * minting and burning.\n *\n * Calling conditions:\n *\n * - when `from` and `to` are both non-zero.\n * - `from` and `to` are never both zero.\n *\n * To learn more about hooks, head to xref:ROOT:extending-contracts.adoc#using-hooks[Using Hooks].\n */\n function _afterTokenTransfer(\n address from,\n address to,\n uint256 tokenId\n ) internal virtual {}\n\n /**\n * @dev This empty reserved space is put in place to allow future versions to add new\n * variables without shifting down storage in the inheritance chain.\n * See https://docs.openzeppelin.com/contracts/4.x/upgradeable#storage_gaps\n */\n uint256[44] private __gap;\n}\n"
},
"@openzeppelin/contracts-upgradeable/utils/ContextUpgradeable.sol": {
"content": "// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts v4.4.1 (utils/Context.sol)\n\npragma solidity ^0.8.0;\nimport \"../proxy/utils/Initializable.sol\";\n\n/**\n * @dev Provides information about the current execution context, including the\n * sender of the transaction and its data. While these are generally available\n * via msg.sender and msg.data, they should not be accessed in such a direct\n * manner, since when dealing with meta-transactions the account sending and\n * paying for execution may not be the actual sender (as far as an application\n * is concerned).\n *\n * This contract is only required for intermediate, library-like contracts.\n */\nabstract contract ContextUpgradeable is Initializable {\n function __Context_init() internal onlyInitializing {\n }\n\n function __Context_init_unchained() internal onlyInitializing {\n }\n function _msgSender() internal view virtual returns (address) {\n return msg.sender;\n }\n\n function _msgData() internal view virtual returns (bytes calldata) {\n return msg.data;\n }\n\n /**\n * @dev This empty reserved space is put in place to allow future versions to add new\n * variables without shifting down storage in the inheritance chain.\n * See https://docs.openzeppelin.com/contracts/4.x/upgradeable#storage_gaps\n */\n uint256[50] private __gap;\n}\n"
},
"@openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol": {
"content": "// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts (last updated v4.7.0) (proxy/utils/Initializable.sol)\n\npragma solidity ^0.8.2;\n\nimport \"../../utils/AddressUpgradeable.sol\";\n\n/**\n * @dev This is a base contract to aid in writing upgradeable contracts, or any kind of contract that will be deployed\n * behind a proxy. Since proxied contracts do not make use of a constructor, it's common to move constructor logic to an\n * external initializer function, usually called `initialize`. It then becomes necessary to protect this initializer\n * function so it can only be called once. The {initializer} modifier provided by this contract will have this effect.\n *\n * The initialization functions use a version number. Once a version number is used, it is consumed and cannot be\n * reused. This mechanism prevents re-execution of each \"step\" but allows the creation of new initialization steps in\n * case an upgrade adds a module that needs to be initialized.\n *\n * For example:\n *\n * [.hljs-theme-light.nopadding]\n * ```\n * contract MyToken is ERC20Upgradeable {\n * function initialize() initializer public {\n * __ERC20_init(\"MyToken\", \"MTK\");\n * }\n * }\n * contract MyTokenV2 is MyToken, ERC20PermitUpgradeable {\n * function initializeV2() reinitializer(2) public {\n * __ERC20Permit_init(\"MyToken\");\n * }\n * }\n * ```\n *\n * TIP: To avoid leaving the proxy in an uninitialized state, the initializer function should be called as early as\n * possible by providing the encoded function call as the `_data` argument to {ERC1967Proxy-constructor}.\n *\n * CAUTION: When used with inheritance, manual care must be taken to not invoke a parent initializer twice, or to ensure\n * that all initializers are idempotent. This is not verified automatically as constructors are by Solidity.\n *\n * [CAUTION]\n * ====\n * Avoid leaving a contract uninitialized.\n *\n * An uninitialized contract can be taken over by an attacker. This applies to both a proxy and its implementation\n * contract, which may impact the proxy. To prevent the implementation contract from being used, you should invoke\n * the {_disableInitializers} function in the constructor to automatically lock it when it is deployed:\n *\n * [.hljs-theme-light.nopadding]\n * ```\n * /// @custom:oz-upgrades-unsafe-allow constructor\n * constructor() {\n * _disableInitializers();\n * }\n * ```\n * ====\n */\nabstract contract Initializable {\n /**\n * @dev Indicates that the contract has been initialized.\n * @custom:oz-retyped-from bool\n */\n uint8 private _initialized;\n\n /**\n * @dev Indicates that the contract is in the process of being initialized.\n */\n bool private _initializing;\n\n /**\n * @dev Triggered when the contract has been initialized or reinitialized.\n */\n event Initialized(uint8 version);\n\n /**\n * @dev A modifier that defines a protected initializer function that can be invoked at most once. In its scope,\n * `onlyInitializing` functions can be used to initialize parent contracts. Equivalent to `reinitializer(1)`.\n */\n modifier initializer() {\n bool isTopLevelCall = !_initializing;\n require(\n (isTopLevelCall && _initialized < 1) || (!AddressUpgradeable.isContract(address(this)) && _initialized == 1),\n \"Initializable: contract is already initialized\"\n );\n _initialized = 1;\n if (isTopLevelCall) {\n _initializing = true;\n }\n _;\n if (isTopLevelCall) {\n _initializing = false;\n emit Initialized(1);\n }\n }\n\n /**\n * @dev A modifier that defines a protected reinitializer function that can be invoked at most once, and only if the\n * contract hasn't been initialized to a greater version before. In its scope, `onlyInitializing` functions can be\n * used to initialize parent contracts.\n *\n * `initializer` is equivalent to `reinitializer(1)`, so a reinitializer may be used after the original\n * initialization step. This is essential to configure modules that are added through upgrades and that require\n * initialization.\n *\n * Note that versions can jump in increments greater than 1; this implies that if multiple reinitializers coexist in\n * a contract, executing them in the right order is up to the developer or operator.\n */\n modifier reinitializer(uint8 version) {\n require(!_initializing && _initialized < version, \"Initializable: contract is already initialized\");\n _initialized = version;\n _initializing = true;\n _;\n _initializing = false;\n emit Initialized(version);\n }\n\n /**\n * @dev Modifier to protect an initialization function so that it can only be invoked by functions with the\n * {initializer} and {reinitializer} modifiers, directly or indirectly.\n */\n modifier onlyInitializing() {\n require(_initializing, \"Initializable: contract is not initializing\");\n _;\n }\n\n /**\n * @dev Locks the contract, preventing any future reinitialization. This cannot be part of an initializer call.\n * Calling this in the constructor of a contract will prevent that contract from being initialized or reinitialized\n * to any version. It is recommended to use this to lock implementation contracts that are designed to be called\n * through proxies.\n */\n function _disableInitializers() internal virtual {\n require(!_initializing, \"Initializable: contract is initializing\");\n if (_initialized < type(uint8).max) {\n _initialized = type(uint8).max;\n emit Initialized(type(uint8).max);\n }\n }\n}\n"
},
"@openzeppelin/contracts-upgradeable/token/ERC721/IERC721Upgradeable.sol": {
"content": "// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts (last updated v4.7.0) (token/ERC721/IERC721.sol)\n\npragma solidity ^0.8.0;\n\nimport \"../../utils/introspection/IERC165Upgradeable.sol\";\n\n/**\n * @dev Required interface of an ERC721 compliant contract.\n */\ninterface IERC721Upgradeable is IERC165Upgradeable {\n /**\n * @dev Emitted when `tokenId` token is transferred from `from` to `to`.\n */\n event Transfer(address indexed from, address indexed to, uint256 indexed tokenId);\n\n /**\n * @dev Emitted when `owner` enables `approved` to manage the `tokenId` token.\n */\n event Approval(address indexed owner, address indexed approved, uint256 indexed tokenId);\n\n /**\n * @dev Emitted when `owner` enables or disables (`approved`) `operator` to manage all of its assets.\n */\n event ApprovalForAll(address indexed owner, address indexed operator, bool approved);\n\n /**\n * @dev Returns the number of tokens in ``owner``'s account.\n */\n function balanceOf(address owner) external view returns (uint256 balance);\n\n /**\n * @dev Returns the owner of the `tokenId` token.\n *\n * Requirements:\n *\n * - `tokenId` must exist.\n */\n function ownerOf(uint256 tokenId) external view returns (address owner);\n\n /**\n * @dev Safely transfers `tokenId` token from `from` to `to`.\n *\n * Requirements:\n *\n * - `from` cannot be the zero address.\n * - `to` cannot be the zero address.\n * - `tokenId` token must exist and be owned by `from`.\n * - If the caller is not `from`, it must be approved to move this token by either {approve} or {setApprovalForAll}.\n * - If `to` refers to a smart contract, it must implement {IERC721Receiver-onERC721Received}, which is called upon a safe transfer.\n *\n * Emits a {Transfer} event.\n */\n function safeTransferFrom(\n address from,\n address to,\n uint256 tokenId,\n bytes calldata data\n ) external;\n\n /**\n * @dev Safely transfers `tokenId` token from `from` to `to`, checking first that contract recipients\n * are aware of the ERC721 protocol to prevent tokens from being forever locked.\n *\n * Requirements:\n *\n * - `from` cannot be the zero address.\n * - `to` cannot be the zero address.\n * - `tokenId` token must exist and be owned by `from`.\n * - If the caller is not `from`, it must have been allowed to move this token by either {approve} or {setApprovalForAll}.\n * - If `to` refers to a smart contract, it must implement {IERC721Receiver-onERC721Received}, which is called upon a safe transfer.\n *\n * Emits a {Transfer} event.\n */\n function safeTransferFrom(\n address from,\n address to,\n uint256 tokenId\n ) external;\n\n /**\n * @dev Transfers `tokenId` token from `from` to `to`.\n *\n * WARNING: Usage of this method is discouraged, use {safeTransferFrom} whenever possible.\n *\n * Requirements:\n *\n * - `from` cannot be the zero address.\n * - `to` cannot be the zero address.\n * - `tokenId` token must be owned by `from`.\n * - If the caller is not `from`, it must be approved to move this token by either {approve} or {setApprovalForAll}.\n *\n * Emits a {Transfer} event.\n */\n function transferFrom(\n address from,\n address to,\n uint256 tokenId\n ) external;\n\n /**\n * @dev Gives permission to `to` to transfer `tokenId` token to another account.\n * The approval is cleared when the token is transferred.\n *\n * Only a single account can be approved at a time, so approving the zero address clears previous approvals.\n *\n * Requirements:\n *\n * - The caller must own the token or be an approved operator.\n * - `tokenId` must exist.\n *\n * Emits an {Approval} event.\n */\n function approve(address to, uint256 tokenId) external;\n\n /**\n * @dev Approve or remove `operator` as an operator for the caller.\n * Operators can call {transferFrom} or {safeTransferFrom} for any token owned by the caller.\n *\n * Requirements:\n *\n * - The `operator` cannot be the caller.\n *\n * Emits an {ApprovalForAll} event.\n */\n function setApprovalForAll(address operator, bool _approved) external;\n\n /**\n * @dev Returns the account approved for `tokenId` token.\n *\n * Requirements:\n *\n * - `tokenId` must exist.\n */\n function getApproved(uint256 tokenId) external view returns (address operator);\n\n /**\n * @dev Returns if the `operator` is allowed to manage all of the assets of `owner`.\n *\n * See {setApprovalForAll}\n */\n function isApprovedForAll(address owner, address operator) external view returns (bool);\n}\n"
},
"@openzeppelin/contracts-upgradeable/token/ERC721/IERC721ReceiverUpgradeable.sol": {
"content": "// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts (last updated v4.6.0) (token/ERC721/IERC721Receiver.sol)\n\npragma solidity ^0.8.0;\n\n/**\n * @title ERC721 token receiver interface\n * @dev Interface for any contract that wants to support safeTransfers\n * from ERC721 asset contracts.\n */\ninterface IERC721ReceiverUpgradeable {\n /**\n * @dev Whenever an {IERC721} `tokenId` token is transferred to this contract via {IERC721-safeTransferFrom}\n * by `operator` from `from`, this function is called.\n *\n * It must return its Solidity selector to confirm the token transfer.\n * If any other value is returned or the interface is not implemented by the recipient, the transfer will be reverted.\n *\n * The selector can be obtained in Solidity with `IERC721Receiver.onERC721Received.selector`.\n */\n function onERC721Received(\n address operator,\n address from,\n uint256 tokenId,\n bytes calldata data\n ) external returns (bytes4);\n}\n"
},
"@openzeppelin/contracts-upgradeable/token/ERC721/extensions/IERC721MetadataUpgradeable.sol": {
"content": "// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts v4.4.1 (token/ERC721/extensions/IERC721Metadata.sol)\n\npragma solidity ^0.8.0;\n\nimport \"../IERC721Upgradeable.sol\";\n\n/**\n * @title ERC-721 Non-Fungible Token Standard, optional metadata extension\n * @dev See https://eips.ethereum.org/EIPS/eip-721\n */\ninterface IERC721MetadataUpgradeable is IERC721Upgradeable {\n /**\n * @dev Returns the token collection name.\n */\n function name() external view returns (string memory);\n\n /**\n * @dev Returns the token collection symbol.\n */\n function symbol() external view returns (string memory);\n\n /**\n * @dev Returns the Uniform Resource Identifier (URI) for `tokenId` token.\n */\n function tokenURI(uint256 tokenId) external view returns (string memory);\n}\n"
},
"@openzeppelin/contracts-upgradeable/utils/AddressUpgradeable.sol": {
"content": "// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts (last updated v4.7.0) (utils/Address.sol)\n\npragma solidity ^0.8.1;\n\n/**\n * @dev Collection of functions related to the address type\n */\nlibrary AddressUpgradeable {\n /**\n * @dev Returns true if `account` is a contract.\n *\n * [IMPORTANT]\n * ====\n * It is unsafe to assume that an address for which this function returns\n * false is an externally-owned account (EOA) and not a contract.\n *\n * Among others, `isContract` will return false for the following\n * types of addresses:\n *\n * - an externally-owned account\n * - a contract in construction\n * - an address where a contract will be created\n * - an address where a contract lived, but was destroyed\n * ====\n *\n * [IMPORTANT]\n * ====\n * You shouldn't rely on `isContract` to protect against flash loan attacks!\n *\n * Preventing calls from contracts is highly discouraged. It breaks composability, breaks support for smart wallets\n * like Gnosis Safe, and does not provide security since it can be circumvented by calling from a contract\n * constructor.\n * ====\n */\n function isContract(address account) internal view returns (bool) {\n // This method relies on extcodesize/address.code.length, which returns 0\n // for contracts in construction, since the code is only stored at the end\n // of the constructor execution.\n\n return account.code.length > 0;\n }\n\n /**\n * @dev Replacement for Solidity's `transfer`: sends `amount` wei to\n * `recipient`, forwarding all available gas and reverting on errors.\n *\n * https://eips.ethereum.org/EIPS/eip-1884[EIP1884] increases the gas cost\n * of certain opcodes, possibly making contracts go over the 2300 gas limit\n * imposed by `transfer`, making them unable to receive funds via\n * `transfer`. {sendValue} removes this limitation.\n *\n * https://diligence.consensys.net/posts/2019/09/stop-using-soliditys-transfer-now/[Learn more].\n *\n * IMPORTANT: because control is transferred to `recipient`, care must be\n * taken to not create reentrancy vulnerabilities. Consider using\n * {ReentrancyGuard} or the\n * https://solidity.readthedocs.io/en/v0.5.11/security-considerations.html#use-the-checks-effects-interactions-pattern[checks-effects-interactions pattern].\n */\n function sendValue(address payable recipient, uint256 amount) internal {\n require(address(this).balance >= amount, \"Address: insufficient balance\");\n\n (bool success, ) = recipient.call{value: amount}(\"\");\n require(success, \"Address: unable to send value, recipient may have reverted\");\n }\n\n /**\n * @dev Performs a Solidity function call using a low level `call`. A\n * plain `call` is an unsafe replacement for a function call: use this\n * function instead.\n *\n * If `target` reverts with a revert reason, it is bubbled up by this\n * function (like regular Solidity function calls).\n *\n * Returns the raw returned data. To convert to the expected return value,\n * use https://solidity.readthedocs.io/en/latest/units-and-global-variables.html?highlight=abi.decode#abi-encoding-and-decoding-functions[`abi.decode`].\n *\n * Requirements:\n *\n * - `target` must be a contract.\n * - calling `target` with `data` must not revert.\n *\n * _Available since v3.1._\n */\n function functionCall(address target, bytes memory data) internal returns (bytes memory) {\n return functionCall(target, data, \"Address: low-level call failed\");\n }\n\n /**\n * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], but with\n * `errorMessage` as a fallback revert reason when `target` reverts.\n *\n * _Available since v3.1._\n */\n function functionCall(\n address target,\n bytes memory data,\n string memory errorMessage\n ) internal returns (bytes memory) {\n return functionCallWithValue(target, data, 0, errorMessage);\n }\n\n /**\n * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],\n * but also transferring `value` wei to `target`.\n *\n * Requirements:\n *\n * - the calling contract must have an ETH balance of at least `value`.\n * - the called Solidity function must be `payable`.\n *\n * _Available since v3.1._\n */\n function functionCallWithValue(\n address target,\n bytes memory data,\n uint256 value\n ) internal returns (bytes memory) {\n return functionCallWithValue(target, data, value, \"Address: low-level call with value failed\");\n }\n\n /**\n * @dev Same as {xref-Address-functionCallWithValue-address-bytes-uint256-}[`functionCallWithValue`], but\n * with `errorMessage` as a fallback revert reason when `target` reverts.\n *\n * _Available since v3.1._\n */\n function functionCallWithValue(\n address target,\n bytes memory data,\n uint256 value,\n string memory errorMessage\n ) internal returns (bytes memory) {\n require(address(this).balance >= value, \"Address: insufficient balance for call\");\n require(isContract(target), \"Address: call to non-contract\");\n\n (bool success, bytes memory returndata) = target.call{value: value}(data);\n return verifyCallResult(success, returndata, errorMessage);\n }\n\n /**\n * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],\n * but performing a static call.\n *\n * _Available since v3.3._\n */\n function functionStaticCall(address target, bytes memory data) internal view returns (bytes memory) {\n return functionStaticCall(target, data, \"Address: low-level static call failed\");\n }\n\n /**\n * @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`],\n * but performing a static call.\n *\n * _Available since v3.3._\n */\n function functionStaticCall(\n address target,\n bytes memory data,\n string memory errorMessage\n ) internal view returns (bytes memory) {\n require(isContract(target), \"Address: static call to non-contract\");\n\n (bool success, bytes memory returndata) = target.staticcall(data);\n return verifyCallResult(success, returndata, errorMessage);\n }\n\n /**\n * @dev Tool to verifies that a low level call was successful, and revert if it wasn't, either by bubbling the\n * revert reason using the provided one.\n *\n * _Available since v4.3._\n */\n function verifyCallResult(\n bool success,\n bytes memory returndata,\n string memory errorMessage\n ) internal pure returns (bytes memory) {\n if (success) {\n return returndata;\n } else {\n // Look for revert reason and bubble it up if present\n if (returndata.length > 0) {\n // The easiest way to bubble the revert reason is using memory via assembly\n /// @solidity memory-safe-assembly\n assembly {\n let returndata_size := mload(returndata)\n revert(add(32, returndata), returndata_size)\n }\n } else {\n revert(errorMessage);\n }\n }\n }\n}\n"
},
"@openzeppelin/contracts-upgradeable/utils/StringsUpgradeable.sol": {
"content": "// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts (last updated v4.7.0) (utils/Strings.sol)\n\npragma solidity ^0.8.0;\n\n/**\n * @dev String operations.\n */\nlibrary StringsUpgradeable {\n bytes16 private constant _HEX_SYMBOLS = \"0123456789abcdef\";\n uint8 private constant _ADDRESS_LENGTH = 20;\n\n /**\n * @dev Converts a `uint256` to its ASCII `string` decimal representation.\n */\n function toString(uint256 value) internal pure returns (string memory) {\n // Inspired by OraclizeAPI's implementation - MIT licence\n // https://github.com/oraclize/ethereum-api/blob/b42146b063c7d6ee1358846c198246239e9360e8/oraclizeAPI_0.4.25.sol\n\n if (value == 0) {\n return \"0\";\n }\n uint256 temp = value;\n uint256 digits;\n while (temp != 0) {\n digits++;\n temp /= 10;\n }\n bytes memory buffer = new bytes(digits);\n while (value != 0) {\n digits -= 1;\n buffer[digits] = bytes1(uint8(48 + uint256(value % 10)));\n value /= 10;\n }\n return string(buffer);\n }\n\n /**\n * @dev Converts a `uint256` to its ASCII `string` hexadecimal representation.\n */\n function toHexString(uint256 value) internal pure returns (string memory) {\n if (value == 0) {\n return \"0x00\";\n }\n uint256 temp = value;\n uint256 length = 0;\n while (temp != 0) {\n length++;\n temp >>= 8;\n }\n return toHexString(value, length);\n }\n\n /**\n * @dev Converts a `uint256` to its ASCII `string` hexadecimal representation with fixed length.\n */\n function toHexString(uint256 value, uint256 length) internal pure returns (string memory) {\n bytes memory buffer = new bytes(2 * length + 2);\n buffer[0] = \"0\";\n buffer[1] = \"x\";\n for (uint256 i = 2 * length + 1; i > 1; --i) {\n buffer[i] = _HEX_SYMBOLS[value & 0xf];\n value >>= 4;\n }\n require(value == 0, \"Strings: hex length insufficient\");\n return string(buffer);\n }\n\n /**\n * @dev Converts an `address` with fixed length of 20 bytes to its not checksummed ASCII `string` hexadecimal representation.\n */\n function toHexString(address addr) internal pure returns (string memory) {\n return toHexString(uint256(uint160(addr)), _ADDRESS_LENGTH);\n }\n}\n"
},
"@openzeppelin/contracts-upgradeable/utils/introspection/ERC165Upgradeable.sol": {
"content": "// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts v4.4.1 (utils/introspection/ERC165.sol)\n\npragma solidity ^0.8.0;\n\nimport \"./IERC165Upgradeable.sol\";\nimport \"../../proxy/utils/Initializable.sol\";\n\n/**\n * @dev Implementation of the {IERC165} interface.\n *\n * Contracts that want to implement ERC165 should inherit from this contract and override {supportsInterface} to check\n * for the additional interface id that will be supported. For example:\n *\n * ```solidity\n * function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) {\n * return interfaceId == type(MyInterface).interfaceId || super.supportsInterface(interfaceId);\n * }\n * ```\n *\n * Alternatively, {ERC165Storage} provides an easier to use but more expensive implementation.\n */\nabstract contract ERC165Upgradeable is Initializable, IERC165Upgradeable {\n function __ERC165_init() internal onlyInitializing {\n }\n\n function __ERC165_init_unchained() internal onlyInitializing {\n }\n /**\n * @dev See {IERC165-supportsInterface}.\n */\n function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) {\n return interfaceId == type(IERC165Upgradeable).interfaceId;\n }\n\n /**\n * @dev This empty reserved space is put in place to allow future versions to add new\n * variables without shifting down storage in the inheritance chain.\n * See https://docs.openzeppelin.com/contracts/4.x/upgradeable#storage_gaps\n */\n uint256[50] private __gap;\n}\n"
},
"@openzeppelin/contracts-upgradeable/utils/introspection/IERC165Upgradeable.sol": {
"content": "// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts v4.4.1 (utils/introspection/IERC165.sol)\n\npragma solidity ^0.8.0;\n\n/**\n * @dev Interface of the ERC165 standard, as defined in the\n * https://eips.ethereum.org/EIPS/eip-165[EIP].\n *\n * Implementers can declare support of contract interfaces, which can then be\n * queried by others ({ERC165Checker}).\n *\n * For an implementation, see {ERC165}.\n */\ninterface IERC165Upgradeable {\n /**\n * @dev Returns true if this contract implements the interface defined by\n * `interfaceId`. See the corresponding\n * https://eips.ethereum.org/EIPS/eip-165#how-interfaces-are-identified[EIP section]\n * to learn more about how these ids are created.\n *\n * This function call must use less than 30 000 gas.\n */\n function supportsInterface(bytes4 interfaceId) external view returns (bool);\n}\n"
}
},
"settings": {
"optimizer": {
"enabled": true,
"runs": 10000
},
"outputSelection": {
"*": {
"*": [
"abi",
"evm.bytecode",
"evm.deployedBytecode",
"evm.methodIdentifiers",
"metadata",
"devdoc",
"userdoc",
"storageLayout",
"evm.gasEstimates"
],
"": [
"ast"
]
}
},
"metadata": {
"useLiteralContent": true
}
}
}
\ No newline at end of file
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment