Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Support
Submit feedback
Contribute to GitLab
Sign in
Toggle navigation
N
nebula
Project
Project
Details
Activity
Releases
Cycle Analytics
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Issues
0
Issues
0
List
Boards
Labels
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Charts
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Charts
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
exchain
nebula
Commits
c28e66ab
Commit
c28e66ab
authored
Jul 20, 2023
by
Andreas Bigger
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
Port op-nft periphery contract to triple slash natspec styling
parent
5ff1f55c
Changes
5
Hide whitespace changes
Inline
Side-by-side
Showing
5 changed files
with
251 additions
and
387 deletions
+251
-387
AttestationStation.sol
...bedrock/contracts/periphery/op-nft/AttestationStation.sol
+21
-39
Optimist.sol
...contracts-bedrock/contracts/periphery/op-nft/Optimist.sol
+60
-100
OptimistAllowlist.sol
...-bedrock/contracts/periphery/op-nft/OptimistAllowlist.sol
+73
-102
OptimistInviter.sol
...ts-bedrock/contracts/periphery/op-nft/OptimistInviter.sol
+94
-138
OptimistConstants.sol
...ontracts/periphery/op-nft/libraries/OptimistConstants.sol
+3
-8
No files found.
packages/contracts-bedrock/contracts/periphery/op-nft/AttestationStation.sol
View file @
c28e66ab
...
@@ -3,39 +3,29 @@ pragma solidity 0.8.15;
...
@@ -3,39 +3,29 @@ pragma solidity 0.8.15;
import { Semver } from "../../universal/Semver.sol";
import { Semver } from "../../universal/Semver.sol";
/**
/// @title AttestationStation
* @title AttestationStation
/// @author Optimism Collective
* @author Optimism Collective
/// @author Gitcoin
* @author Gitcoin
/// @notice Where attestations live.
* @notice Where attestations live.
*/
contract AttestationStation is Semver {
contract AttestationStation is Semver {
/**
/// @notice Struct representing data that is being attested.
* @notice Struct representing data that is being attested.
/// @custom:field about Address for which the attestation is about.
*
/// @custom:field key A bytes32 key for the attestation.
* @custom:field about Address for which the attestation is about.
/// @custom:field val The attestation as arbitrary bytes.
* @custom:field key A bytes32 key for the attestation.
* @custom:field val The attestation as arbitrary bytes.
*/
struct AttestationData {
struct AttestationData {
address about;
address about;
bytes32 key;
bytes32 key;
bytes val;
bytes val;
}
}
/**
/// @notice Maps addresses to attestations. Creator => About => Key => Value.
* @notice Maps addresses to attestations. Creator => About => Key => Value.
*/
mapping(address => mapping(address => mapping(bytes32 => bytes))) public attestations;
mapping(address => mapping(address => mapping(bytes32 => bytes))) public attestations;
/**
/// @notice Emitted when Attestation is created.
* @notice Emitted when Attestation is created.
/// @param creator Address that made the attestation.
*
/// @param about Address attestation is about.
* @param creator Address that made the attestation.
/// @param key Key of the attestation.
* @param about Address attestation is about.
/// @param val Value of the attestation.
* @param key Key of the attestation.
* @param val Value of the attestation.
*/
event AttestationCreated(
event AttestationCreated(
address indexed creator,
address indexed creator,
address indexed about,
address indexed about,
...
@@ -43,18 +33,13 @@ contract AttestationStation is Semver {
...
@@ -43,18 +33,13 @@ contract AttestationStation is Semver {
bytes val
bytes val
);
);
/**
/// @custom:semver 1.1.0
* @custom:semver 1.1.0
*/
constructor() Semver(1, 1, 0) {}
constructor() Semver(1, 1, 0) {}
/**
/// @notice Allows anyone to create an attestation.
* @notice Allows anyone to create an attestation.
/// @param _about Address that the attestation is about.
*
/// @param _key A key used to namespace the attestation.
* @param _about Address that the attestation is about.
/// @param _val An arbitrary value stored as part of the attestation.
* @param _key A key used to namespace the attestation.
* @param _val An arbitrary value stored as part of the attestation.
*/
function attest(
function attest(
address _about,
address _about,
bytes32 _key,
bytes32 _key,
...
@@ -65,11 +50,8 @@ contract AttestationStation is Semver {
...
@@ -65,11 +50,8 @@ contract AttestationStation is Semver {
emit AttestationCreated(msg.sender, _about, _key, _val);
emit AttestationCreated(msg.sender, _about, _key, _val);
}
}
/**
/// @notice Allows anyone to create attestations.
* @notice Allows anyone to create attestations.
/// @param _attestations An array of AttestationData structs.
*
* @param _attestations An array of AttestationData structs.
*/
function attest(AttestationData[] calldata _attestations) external {
function attest(AttestationData[] calldata _attestations) external {
uint256 length = _attestations.length;
uint256 length = _attestations.length;
for (uint256 i = 0; i < length; ) {
for (uint256 i = 0; i < length; ) {
...
...
packages/contracts-bedrock/contracts/periphery/op-nft/Optimist.sol
View file @
c28e66ab
...
@@ -9,41 +9,29 @@ import { AttestationStation } from "./AttestationStation.sol";
...
@@ -9,41 +9,29 @@ import { AttestationStation } from "./AttestationStation.sol";
import { OptimistAllowlist } from "./OptimistAllowlist.sol";
import { OptimistAllowlist } from "./OptimistAllowlist.sol";
import { Strings } from "@openzeppelin/contracts/utils/Strings.sol";
import { Strings } from "@openzeppelin/contracts/utils/Strings.sol";
/**
/// @author Optimism Collective
* @author Optimism Collective
/// @author Gitcoin
* @author Gitcoin
/// @title Optimist
* @title Optimist
/// @notice A Soul Bound Token for real humans only(tm).
* @notice A Soul Bound Token for real humans only(tm).
*/
contract Optimist is ERC721BurnableUpgradeable, Semver {
contract Optimist is ERC721BurnableUpgradeable, Semver {
/**
/// @notice Attestation key used by the attestor to attest the baseURI.
* @notice Attestation key used by the attestor to attest the baseURI.
*/
bytes32 public constant BASE_URI_ATTESTATION_KEY = bytes32("optimist.base-uri");
bytes32 public constant BASE_URI_ATTESTATION_KEY = bytes32("optimist.base-uri");
/**
/// @notice Attestor who attests to baseURI.
* @notice Attestor who attests to baseURI.
*/
address public immutable BASE_URI_ATTESTOR;
address public immutable BASE_URI_ATTESTOR;
/**
/// @notice Address of the AttestationStation contract.
* @notice Address of the AttestationStation contract.
*/
AttestationStation public immutable ATTESTATION_STATION;
AttestationStation public immutable ATTESTATION_STATION;
/**
/// @notice Address of the OptimistAllowlist contract.
* @notice Address of the OptimistAllowlist contract.
*/
OptimistAllowlist public immutable OPTIMIST_ALLOWLIST;
OptimistAllowlist public immutable OPTIMIST_ALLOWLIST;
/**
/// @custom:semver 2.0.0
* @custom:semver 2.0.0
/// @param _name Token name.
* @param _name Token name.
/// @param _symbol Token symbol.
* @param _symbol Token symbol.
/// @param _baseURIAttestor Address of the baseURI attestor.
* @param _baseURIAttestor Address of the baseURI attestor.
/// @param _attestationStation Address of the AttestationStation contract.
* @param _attestationStation Address of the AttestationStation contract.
/// @param _optimistAllowlist Address of the OptimistAllowlist contract
* @param _optimistAllowlist Address of the OptimistAllowlist contract
*/
constructor(
constructor(
string memory _name,
string memory _name,
string memory _symbol,
string memory _symbol,
...
@@ -57,109 +45,81 @@ contract Optimist is ERC721BurnableUpgradeable, Semver {
...
@@ -57,109 +45,81 @@ contract Optimist is ERC721BurnableUpgradeable, Semver {
initialize(_name, _symbol);
initialize(_name, _symbol);
}
}
/**
/// @notice Initializes the Optimist contract.
* @notice Initializes the Optimist contract.
/// @param _name Token name.
*
/// @param _symbol Token symbol.
* @param _name Token name.
* @param _symbol Token symbol.
*/
function initialize(string memory _name, string memory _symbol) public initializer {
function initialize(string memory _name, string memory _symbol) public initializer {
__ERC721_init(_name, _symbol);
__ERC721_init(_name, _symbol);
__ERC721Burnable_init();
__ERC721Burnable_init();
}
}
/**
/// @notice Allows an address to mint an Optimist NFT. Token ID is the uint256 representation
* @notice Allows an address to mint an Optimist NFT. Token ID is the uint256 representation
/// of the recipient's address. Recipients must be permitted to mint, eventually anyone
* of the recipient's address. Recipients must be permitted to mint, eventually anyone
/// will be able to mint. One token per address.
* will be able to mint. One token per address.
/// @param _recipient Address of the token recipient.
*
* @param _recipient Address of the token recipient.
*/
function mint(address _recipient) public {
function mint(address _recipient) public {
require(isOnAllowList(_recipient), "Optimist: address is not on allowList");
require(isOnAllowList(_recipient), "Optimist: address is not on allowList");
_safeMint(_recipient, tokenIdOfAddress(_recipient));
_safeMint(_recipient, tokenIdOfAddress(_recipient));
}
}
/**
/// @notice Returns the baseURI for all tokens.
* @notice Returns the baseURI for all tokens.
/// @return uri_ BaseURI for all tokens.
*
function baseURI() public view returns (string memory uri_) {
* @return BaseURI for all tokens.
uri_ = string(
*/
abi.encodePacked(
function baseURI() public view returns (string memory) {
ATTESTATION_STATION.attestations(
return
BASE_URI_ATTESTOR,
string(
address(this),
abi.encodePacked(
bytes32("optimist.base-uri")
ATTESTATION_STATION.attestations(
BASE_URI_ATTESTOR,
address(this),
bytes32("optimist.base-uri")
)
)
)
);
)
);
}
}
/**
/// @notice Returns the token URI for a given token by ID
* @notice Returns the token URI for a given token by ID
/// @param _tokenId Token ID to query.
*
/// @return uri_ Token URI for the given token by ID.
* @param _tokenId Token ID to query.
function tokenURI(uint256 _tokenId) public view virtual override returns (string memory uri_) {
uri_ = string(
* @return Token URI for the given token by ID.
abi.encodePacked(
*/
baseURI(),
function tokenURI(uint256 _tokenId) public view virtual override returns (string memory) {
"/",
return
// Properly format the token ID as a 20 byte hex string (address).
string(
Strings.toHexString(_tokenId, 20),
abi.encodePacked(
".json"
baseURI(),
)
"/",
);
// Properly format the token ID as a 20 byte hex string (address).
Strings.toHexString(_tokenId, 20),
".json"
)
);
}
}
/**
/// @notice Checks OptimistAllowlist to determine whether a given address is allowed to mint
* @notice Checks OptimistAllowlist to determine whether a given address is allowed to mint
/// the Optimist NFT. Since the Optimist NFT will also be used as part of the
* the Optimist NFT. Since the Optimist NFT will also be used as part of the
/// Citizens House, mints are currently restricted. Eventually anyone will be able
* Citizens House, mints are currently restricted. Eventually anyone will be able
/// to mint.
* to mint.
/// @return allowed_ Whether or not the address is allowed to mint yet.
*
function isOnAllowList(address _recipient) public view returns (bool allowed_) {
* @return Whether or not the address is allowed to mint yet.
allowed_ = OPTIMIST_ALLOWLIST.isAllowedToMint(_recipient);
*/
function isOnAllowList(address _recipient) public view returns (bool) {
return OPTIMIST_ALLOWLIST.isAllowedToMint(_recipient);
}
}
/**
/// @notice Returns the token ID for the token owned by a given address. This is the uint256
* @notice Returns the token ID for the token owned by a given address. This is the uint256
/// representation of the given address.
* representation of the given address.
/// @return Token ID for the token owned by the given address.
*
* @return Token ID for the token owned by the given address.
*/
function tokenIdOfAddress(address _owner) public pure returns (uint256) {
function tokenIdOfAddress(address _owner) public pure returns (uint256) {
return uint256(uint160(_owner));
return uint256(uint160(_owner));
}
}
/**
/// @notice Disabled for the Optimist NFT (Soul Bound Token).
* @notice Disabled for the Optimist NFT (Soul Bound Token).
*/
function approve(address, uint256) public pure override {
function approve(address, uint256) public pure override {
revert("Optimist: soul bound token");
revert("Optimist: soul bound token");
}
}
/**
/// @notice Disabled for the Optimist NFT (Soul Bound Token).
* @notice Disabled for the Optimist NFT (Soul Bound Token).
*/
function setApprovalForAll(address, bool) public virtual override {
function setApprovalForAll(address, bool) public virtual override {
revert("Optimist: soul bound token");
revert("Optimist: soul bound token");
}
}
/**
/// @notice Prevents transfers of the Optimist NFT (Soul Bound Token).
* @notice Prevents transfers of the Optimist NFT (Soul Bound Token).
/// @param _from Address of the token sender.
*
/// @param _to Address of the token recipient.
* @param _from Address of the token sender.
* @param _to Address of the token recipient.
*/
function _beforeTokenTransfer(
function _beforeTokenTransfer(
address _from,
address _from,
address _to,
address _to,
...
...
packages/contracts-bedrock/contracts/periphery/op-nft/OptimistAllowlist.sol
View file @
c28e66ab
...
@@ -5,54 +5,37 @@ import { Semver } from "../../universal/Semver.sol";
...
@@ -5,54 +5,37 @@ import { Semver } from "../../universal/Semver.sol";
import { AttestationStation } from "./AttestationStation.sol";
import { AttestationStation } from "./AttestationStation.sol";
import { OptimistConstants } from "./libraries/OptimistConstants.sol";
import { OptimistConstants } from "./libraries/OptimistConstants.sol";
/**
/// @title OptimistAllowlist
* @title OptimistAllowlist
/// @notice Source of truth for whether an address is able to mint an Optimist NFT.
* @notice Source of truth for whether an address is able to mint an Optimist NFT.
/// isAllowedToMint function checks various signals to return boolean value
isAllowedToMint function checks various signals to return boolean value for whether an
/// for whether an address is eligible or not.
address is eligible or not.
*/
contract OptimistAllowlist is Semver {
contract OptimistAllowlist is Semver {
/**
/// @notice Attestation key used by the AllowlistAttestor to manually add addresses to the
* @notice Attestation key used by the AllowlistAttestor to manually add addresses to the
/// allowlist.
* allowlist.
*/
bytes32 public constant OPTIMIST_CAN_MINT_ATTESTATION_KEY = bytes32("optimist.can-mint");
bytes32 public constant OPTIMIST_CAN_MINT_ATTESTATION_KEY = bytes32("optimist.can-mint");
/**
/// @notice Attestation key used by Coinbase to issue attestations for Quest participants.
* @notice Attestation key used by Coinbase to issue attestations for Quest participants.
*/
bytes32 public constant COINBASE_QUEST_ELIGIBLE_ATTESTATION_KEY =
bytes32 public constant COINBASE_QUEST_ELIGIBLE_ATTESTATION_KEY =
bytes32("coinbase.quest-eligible");
bytes32("coinbase.quest-eligible");
/**
/// @notice Address of the AttestationStation contract.
* @notice Address of the AttestationStation contract.
*/
AttestationStation public immutable ATTESTATION_STATION;
AttestationStation public immutable ATTESTATION_STATION;
/**
/// @notice Attestor that issues 'optimist.can-mint' attestations.
* @notice Attestor that issues 'optimist.can-mint' attestations.
*/
address public immutable ALLOWLIST_ATTESTOR;
address public immutable ALLOWLIST_ATTESTOR;
/**
/// @notice Attestor that issues 'coinbase.quest-eligible' attestations.
* @notice Attestor that issues 'coinbase.quest-eligible' attestations.
*/
address public immutable COINBASE_QUEST_ATTESTOR;
address public immutable COINBASE_QUEST_ATTESTOR;
/**
/// @notice Address of OptimistInviter contract that issues 'optimist.can-mint-from-invite'
* @notice Address of OptimistInviter contract that issues 'optimist.can-mint-from-invite'
/// attestations.
* attestations.
*/
address public immutable OPTIMIST_INVITER;
address public immutable OPTIMIST_INVITER;
/**
/// @custom:semver 1.0.0
* @custom:semver 1.0.0
/// @param _attestationStation Address of the AttestationStation contract.
*
/// @param _allowlistAttestor Address of the allowlist attestor.
* @param _attestationStation Address of the AttestationStation contract.
/// @param _coinbaseQuestAttestor Address of the Coinbase Quest attestor.
* @param _allowlistAttestor Address of the allowlist attestor.
/// @param _optimistInviter Address of the OptimistInviter contract.
* @param _coinbaseQuestAttestor Address of the Coinbase Quest attestor.
* @param _optimistInviter Address of the OptimistInviter contract.
*/
constructor(
constructor(
AttestationStation _attestationStation,
AttestationStation _attestationStation,
address _allowlistAttestor,
address _allowlistAttestor,
...
@@ -65,95 +48,83 @@ contract OptimistAllowlist is Semver {
...
@@ -65,95 +48,83 @@ contract OptimistAllowlist is Semver {
OPTIMIST_INVITER = _optimistInviter;
OPTIMIST_INVITER = _optimistInviter;
}
}
/**
/// @notice Checks whether a given address is allowed to mint the Optimist NFT yet. Since the
* @notice Checks whether a given address is allowed to mint the Optimist NFT yet. Since the
/// Optimist NFT will also be used as part of the Citizens House, mints are currently
* Optimist NFT will also be used as part of the Citizens House, mints are currently
/// restricted. Eventually anyone will be able to mint.
* restricted. Eventually anyone will be able to mint.
/// Currently, address is allowed to mint if it satisfies any of the following:
*
/// 1) Has a valid 'optimist.can-mint' attestation from the allowlist attestor.
* Currently, address is allowed to mint if it satisfies any of the following:
/// 2) Has a valid 'coinbase.quest-eligible' attestation from Coinbase Quest attestor
* 1) Has a valid 'optimist.can-mint' attestation from the allowlist attestor.
/// 3) Has a valid 'optimist.can-mint-from-invite' attestation from the OptimistInviter
* 2) Has a valid 'coinbase.quest-eligible' attestation from Coinbase Quest attestor
/// contract.
* 3) Has a valid 'optimist.can-mint-from-invite' attestation from the OptimistInviter
/// @param _claimer Address to check.
* contract.
/// @return allowed_ Whether or not the address is allowed to mint yet.
*
function isAllowedToMint(address _claimer) public view returns (bool allowed_) {
* @param _claimer Address to check.
allowed_ =
*
* @return Whether or not the address is allowed to mint yet.
*/
function isAllowedToMint(address _claimer) public view returns (bool) {
return
_hasAttestationFromAllowlistAttestor(_claimer) ||
_hasAttestationFromAllowlistAttestor(_claimer) ||
_hasAttestationFromCoinbaseQuestAttestor(_claimer) ||
_hasAttestationFromCoinbaseQuestAttestor(_claimer) ||
_hasAttestationFromOptimistInviter(_claimer);
_hasAttestationFromOptimistInviter(_claimer);
}
}
/
**
/
// @notice Checks whether an address has a valid 'optimist.can-mint' attestation from the
* @notice Checks whether an address has a valid 'optimist.can-mint' attestation from the
/// allowlist attestor.
* allowlist attestor
.
/// @param _claimer Address to check
.
*
/// @return valid_ Whether or not the address has a valid attestation.
* @param _claimer Address to check.
function _hasAttestationFromAllowlistAttestor(address _claimer)
*
internal
* @return Whether or not the address has a valid attestation.
view
*/
returns (bool valid_)
function _hasAttestationFromAllowlistAttestor(address _claimer) internal view returns (bool)
{
{
// Expected attestation value is bytes32("true")
// Expected attestation value is bytes32("true")
return
valid_ = _hasValidAttestation(
_hasValidAttestation(ALLOWLIST_ATTESTOR, _claimer, OPTIMIST_CAN_MINT_ATTESTATION_KEY);
ALLOWLIST_ATTESTOR,
_claimer,
OPTIMIST_CAN_MINT_ATTESTATION_KEY
);
}
}
/**
/// @notice Checks whether an address has a valid attestation from the Coinbase attestor.
* @notice Checks whether an address has a valid attestation from the Coinbase attestor.
/// @param _claimer Address to check.
*
/// @return valid_ Whether or not the address has a valid attestation.
* @param _claimer Address to check.
*
* @return Whether or not the address has a valid attestation.
*/
function _hasAttestationFromCoinbaseQuestAttestor(address _claimer)
function _hasAttestationFromCoinbaseQuestAttestor(address _claimer)
internal
internal
view
view
returns (bool)
returns (bool
valid_
)
{
{
// Expected attestation value is bytes32("true")
// Expected attestation value is bytes32("true")
return
valid_ = _hasValidAttestation(
_hasValidAttestation(
COINBASE_QUEST_ATTESTOR,
COINBASE_QUEST_ATTESTOR,
_claimer,
_claimer,
COINBASE_QUEST_ELIGIBLE_ATTESTATION_KEY
COINBASE_QUEST_ELIGIBLE_ATTESTATION_KEY
);
);
}
}
/
**
/
// @notice Checks whether an address has a valid attestation from the OptimistInviter contract.
* @notice Checks whether an address has a valid attestation from the OptimistInviter contract
.
/// @param _claimer Address to check
.
*
/// @return valid_ Whether or not the address has a valid attestation.
* @param _claimer Address to check.
function _hasAttestationFromOptimistInviter(address _claimer)
*
internal
* @return Whether or not the address has a valid attestation.
view
*/
returns (bool valid_)
function _hasAttestationFromOptimistInviter(address _claimer) internal view returns (bool)
{
{
// Expected attestation value is the inviter's address
// Expected attestation value is the inviter's address
return
valid_ = _hasValidAttestation(
_hasValidAttestation(
OPTIMIST_INVITER,
OPTIMIST_INVITER,
_claimer,
_claimer,
OptimistConstants.OPTIMIST_CAN_MINT_FROM_INVITE_ATTESTATION_KEY
OptimistConstants.OPTIMIST_CAN_MINT_FROM_INVITE_ATTESTATION_KEY
);
);
}
}
/**
/// @notice Checks whether an address has a valid truthy attestation.
* @notice Checks whether an address has a valid truthy attestation.
/// Any attestation val other than bytes32("") is considered truthy.
* Any attestation val other than bytes32("") is considered truthy.
/// @param _creator Address that made the attestation.
*
/// @param _about Address attestation is about.
* @param _creator Address that made the attestation.
/// @param _key Key of the attestation.
* @param _about Address attestation is about.
/// @return valid_ Whether or not the address has a valid truthy attestation.
* @param _key Key of the attestation.
*
* @return Whether or not the address has a valid truthy attestation.
*/
function _hasValidAttestation(
function _hasValidAttestation(
address _creator,
address _creator,
address _about,
address _about,
bytes32 _key
bytes32 _key
) internal view returns (bool) {
) internal view returns (bool
valid_
) {
return
ATTESTATION_STATION.attestations(_creator, _about, _key).length > 0;
valid_ =
ATTESTATION_STATION.attestations(_creator, _about, _key).length > 0;
}
}
}
}
packages/contracts-bedrock/contracts/periphery/op-nft/OptimistInviter.sol
View file @
c28e66ab
...
@@ -9,144 +9,108 @@ import {
...
@@ -9,144 +9,108 @@ import {
EIP712Upgradeable
EIP712Upgradeable
} from "@openzeppelin/contracts-upgradeable/utils/cryptography/draft-EIP712Upgradeable.sol";
} from "@openzeppelin/contracts-upgradeable/utils/cryptography/draft-EIP712Upgradeable.sol";
/**
/// @custom:upgradeable
* @custom:upgradeable
/// @title OptimistInviter
* @title OptimistInviter
/// @notice OptimistInviter issues "optimist.can-invite" and "optimist.can-mint-from-invite"
* @notice OptimistInviter issues "optimist.can-invite" and "optimist.can-mint-from-invite"
/// attestations. Accounts that have invites can issue signatures that allow other
* attestations. Accounts that have invites can issue signatures that allow other
/// accounts to claim an invite. The invitee uses a claim and reveal flow to claim the
* accounts to claim an invite. The invitee uses a claim and reveal flow to claim the
/// invite to an address of their choosing.
* invite to an address of their choosing.
///
*
/// Parties involved:
* Parties involved:
/// 1) INVITE_GRANTER: trusted account that can allow accounts to issue invites
* 1) INVITE_GRANTER: trusted account that can allow accounts to issue invites
/// 2) issuer: account that is allowed to issue invites
* 2) issuer: account that is allowed to issue invites
/// 3) claimer: account that receives the invites
* 3) claimer: account that receives the invites
///
*
/// Flow:
* Flow:
/// 1) INVITE_GRANTER calls _setInviteCount to allow an issuer to issue a certain number
* 1) INVITE_GRANTER calls _setInviteCount to allow an issuer to issue a certain number
/// of invites, and also creates a "optimist.can-invite" attestation for the issuer
* of invites, and also creates a "optimist.can-invite" attestation for the issuer
/// 2) Off-chain, the issuer signs (EIP-712) a ClaimableInvite to produce a signature
* 2) Off-chain, the issuer signs (EIP-712) a ClaimableInvite to produce a signature
/// 3) Off-chain, invite issuer sends the plaintext ClaimableInvite and the signature
* 3) Off-chain, invite issuer sends the plaintext ClaimableInvite and the signature
/// to the recipient
* to the recipient
/// 4) claimer chooses an address they want to receive the invite on
* 4) claimer chooses an address they want to receive the invite on
/// 5) claimer commits the hash of the address they want to receive the invite on and the
* 5) claimer commits the hash of the address they want to receive the invite on and the
/// received signature keccak256(abi.encode(addressToReceiveTo, receivedSignature))
* received signature keccak256(abi.encode(addressToReceiveTo, receivedSignature))
/// using the commitInvite function
* using the commitInvite function
/// 6) claimer waits for the MIN_COMMITMENT_PERIOD to pass.
* 6) claimer waits for the MIN_COMMITMENT_PERIOD to pass.
/// 7) claimer reveals the plaintext ClaimableInvite and the signature using the
* 7) claimer reveals the plaintext ClaimableInvite and the signature using the
/// claimInvite function, receiving the "optimist.can-mint-from-invite" attestation
* claimInvite function, receiving the "optimist.can-mint-from-invite" attestation
*/
contract OptimistInviter is Semver, EIP712Upgradeable {
contract OptimistInviter is Semver, EIP712Upgradeable {
/**
/// @notice Emitted when an invite is claimed.
* @notice Emitted when an invite is claimed.
/// @param issuer Address that issued the signature.
*
/// @param claimer Address that claimed the invite.
* @param issuer Address that issued the signature.
* @param claimer Address that claimed the invite.
*/
event InviteClaimed(address indexed issuer, address indexed claimer);
event InviteClaimed(address indexed issuer, address indexed claimer);
/**
/// @notice Version used for the EIP712 domain separator. This version is separated from the
* @notice Version used for the EIP712 domain separator. This version is separated from the
/// contract semver because the EIP712 domain separator is used to sign messages, and
* contract semver because the EIP712 domain separator is used to sign messages, and
/// changing the domain separator invalidates all existing signatures. We should only
* changing the domain separator invalidates all existing signatures. We should only
/// bump this version if we make a major change to the signature scheme.
* bump this version if we make a major change to the signature scheme.
*/
string public constant EIP712_VERSION = "1.0.0";
string public constant EIP712_VERSION = "1.0.0";
/**
/// @notice EIP712 typehash for the ClaimableInvite type.
* @notice EIP712 typehash for the ClaimableInvite type.
*/
bytes32 public constant CLAIMABLE_INVITE_TYPEHASH =
bytes32 public constant CLAIMABLE_INVITE_TYPEHASH =
keccak256("ClaimableInvite(address issuer,bytes32 nonce)");
keccak256("ClaimableInvite(address issuer,bytes32 nonce)");
/**
/// @notice Attestation key for that signals that an account was allowed to issue invites
* @notice Attestation key for that signals that an account was allowed to issue invites
*/
bytes32 public constant CAN_INVITE_ATTESTATION_KEY = bytes32("optimist.can-invite");
bytes32 public constant CAN_INVITE_ATTESTATION_KEY = bytes32("optimist.can-invite");
/**
/// @notice Granter who can set accounts' invite counts.
* @notice Granter who can set accounts' invite counts.
*/
address public immutable INVITE_GRANTER;
address public immutable INVITE_GRANTER;
/**
/// @notice Address of the AttestationStation contract.
* @notice Address of the AttestationStation contract.
*/
AttestationStation public immutable ATTESTATION_STATION;
AttestationStation public immutable ATTESTATION_STATION;
/**
/// @notice Minimum age of a commitment (in seconds) before it can be revealed using
* @notice Minimum age of a commitment (in seconds) before it can be revealed using claimInvite.
/// claimInvite. Currently set to 60 seconds.
* Currently set to 60 seconds.
///
*
/// Prevents an attacker from front-running a commitment by taking the signature in the
* Prevents an attacker from front-running a commitment by taking the signature in the
/// claimInvite call and quickly committing and claiming it before the the claimer's
* claimInvite call and quickly committing and claiming it before the the claimer's
/// transaction succeeds. With this, frontrunning a commitment requires that an attacker
* transaction succeeds. With this, frontrunning a commitment requires that an attacker
/// be able to prevent the honest claimer's claimInvite transaction from being included
* be able to prevent the honest claimer's claimInvite transaction from being included
/// for this long.
* for this long.
*/
uint256 public constant MIN_COMMITMENT_PERIOD = 60;
uint256 public constant MIN_COMMITMENT_PERIOD = 60;
/**
/// @notice Struct that represents a claimable invite that will be signed by the issuer.
* @notice Struct that represents a claimable invite that will be signed by the issuer.
/// @custom:field issuer Address that issued the signature. Reason this is explicitly included,
*
/// and not implicitly assumed to be the recovered address from the
* @custom:field issuer Address that issued the signature. Reason this is explicitly included,
/// signature is that the issuer may be using a ERC-1271 compatible
* and not implicitly assumed to be the recovered address from the
/// contract wallet, where the recovered address is not the same as the
* signature is that the issuer may be using a ERC-1271 compatible
/// issuer, or the signature is not an ECDSA signature at all.
* contract wallet, where the recovered address is not the same as the
/// @custom:field nonce Pseudorandom nonce to prevent replay attacks.
* issuer, or the signature is not an ECDSA signature at all.
* @custom:field nonce Pseudorandom nonce to prevent replay attacks.
*/
struct ClaimableInvite {
struct ClaimableInvite {
address issuer;
address issuer;
bytes32 nonce;
bytes32 nonce;
}
}
/**
/// @notice Maps from hashes to the timestamp when they were committed.
* @notice Maps from hashes to the timestamp when they were committed.
*/
mapping(bytes32 => uint256) public commitmentTimestamps;
mapping(bytes32 => uint256) public commitmentTimestamps;
/**
/// @notice Maps from addresses to nonces to whether or not they have been used.
* @notice Maps from addresses to nonces to whether or not they have been used.
*/
mapping(address => mapping(bytes32 => bool)) public usedNonces;
mapping(address => mapping(bytes32 => bool)) public usedNonces;
/**
/// @notice Maps from addresses to number of invites they have.
* @notice Maps from addresses to number of invites they have.
*/
mapping(address => uint256) public inviteCounts;
mapping(address => uint256) public inviteCounts;
/**
/// @custom:semver 1.0.0
* @custom:semver 1.0.0
/// @param _inviteGranter Address of the invite granter.
*
/// @param _attestationStation Address of the AttestationStation contract.
* @param _inviteGranter Address of the invite granter.
* @param _attestationStation Address of the AttestationStation contract.
*/
constructor(address _inviteGranter, AttestationStation _attestationStation) Semver(1, 0, 0) {
constructor(address _inviteGranter, AttestationStation _attestationStation) Semver(1, 0, 0) {
INVITE_GRANTER = _inviteGranter;
INVITE_GRANTER = _inviteGranter;
ATTESTATION_STATION = _attestationStation;
ATTESTATION_STATION = _attestationStation;
}
}
/**
/// @notice Initializes this contract, setting the EIP712 context.
* @notice Initializes this contract, setting the EIP712 context.
/// Only update the EIP712_VERSION when there is a change to the signature scheme.
*
/// After the EIP712 version is changed, any signatures issued off-chain but not
* Only update the EIP712_VERSION when there is a change to the signature scheme.
/// claimed yet will no longer be accepted by the claimInvite function. Please make
* After the EIP712 version is changed, any signatures issued off-chain but not
/// sure to notify the issuers that they must re-issue their invite signatures.
* claimed yet will no longer be accepted by the claimInvite function. Please make
/// @param _name Contract name.
* sure to notify the issuers that they must re-issue their invite signatures.
*
* @param _name Contract name.
*/
function initialize(string memory _name) public initializer {
function initialize(string memory _name) public initializer {
__EIP712_init(_name, EIP712_VERSION);
__EIP712_init(_name, EIP712_VERSION);
}
}
/**
/// @notice Allows invite granter to set the number of invites an address has.
* @notice Allows invite granter to set the number of invites an address has.
/// @param _accounts An array of accounts to update the invite counts of.
*
/// @param _inviteCount Number of invites to set to.
* @param _accounts An array of accounts to update the invite counts of.
* @param _inviteCount Number of invites to set to.
*/
function setInviteCounts(address[] calldata _accounts, uint256 _inviteCount) public {
function setInviteCounts(address[] calldata _accounts, uint256 _inviteCount) public {
// Only invite granter can grant invites
// Only invite granter can grant invites
require(
require(
...
@@ -178,25 +142,21 @@ contract OptimistInviter is Semver, EIP712Upgradeable {
...
@@ -178,25 +142,21 @@ contract OptimistInviter is Semver, EIP712Upgradeable {
ATTESTATION_STATION.attest(attestations);
ATTESTATION_STATION.attest(attestations);
}
}
/**
/// @notice Allows anyone (but likely the claimer) to commit a received signature along with the
* @notice Allows anyone (but likely the claimer) to commit a received signature along with the
/// address to claim to.
* address to claim to.
///
*
/// Before calling this function, the claimer should have received a signature from the
* Before calling this function, the claimer should have received a signature from the
/// issuer off-chain. The claimer then calls this function with the hash of the
* issuer off-chain. The claimer then calls this function with the hash of the
/// claimer's address and the received signature. This is necessary to prevent
* claimer's address and the received signature. This is necessary to prevent
/// front-running when the invitee is claiming the invite. Without a commit and reveal
* front-running when the invitee is claiming the invite. Without a commit and reveal
/// scheme, anyone who is watching the mempool can take the signature being submitted
* scheme, anyone who is watching the mempool can take the signature being submitted
/// and front run the transaction to claim the invite to their own address.
* and front run the transaction to claim the invite to their own address.
///
*
/// The same commitment can only be made once, and the function reverts if the
* The same commitment can only be made once, and the function reverts if the
/// commitment has already been made. This prevents griefing where a malicious party can
* commitment has already been made. This prevents griefing where a malicious party can
/// prevent the original claimer from being able to claimInvite.
* prevent the original claimer from being able to claimInvite.
/// @param _commitment A hash of the claimer and signature concatenated.
*
/// keccak256(abi.encode(_claimer, _signature))
*
* @param _commitment A hash of the claimer and signature concatenated.
* keccak256(abi.encode(_claimer, _signature))
*/
function commitInvite(bytes32 _commitment) public {
function commitInvite(bytes32 _commitment) public {
// Check that the commitment hasn't already been made. This prevents griefing where
// Check that the commitment hasn't already been made. This prevents griefing where
// a malicious party continuously re-submits the same commitment, preventing the original
// a malicious party continuously re-submits the same commitment, preventing the original
...
@@ -206,23 +166,19 @@ contract OptimistInviter is Semver, EIP712Upgradeable {
...
@@ -206,23 +166,19 @@ contract OptimistInviter is Semver, EIP712Upgradeable {
commitmentTimestamps[_commitment] = block.timestamp;
commitmentTimestamps[_commitment] = block.timestamp;
}
}
/**
/// @notice Allows anyone to reveal a commitment and claim an invite.
* @notice Allows anyone to reveal a commitment and claim an invite.
/// The hash, keccak256(abi.encode(_claimer, _signature)), should have been already
*
/// committed using commitInvite. Before issuing the "optimist.can-mint-from-invite"
* The hash, keccak256(abi.encode(_claimer, _signature)), should have been already
/// attestation, this function checks that
* committed using commitInvite. Before issuing the "optimist.can-mint-from-invite"
/// 1) the hash corresponding to the _claimer and the _signature was committed
* attestation, this function checks that
/// 2) MIN_COMMITMENT_PERIOD has passed since the commitment was made.
* 1) the hash corresponding to the _claimer and the _signature was committed
/// 3) the _signature is signed correctly by the issuer
* 2) MIN_COMMITMENT_PERIOD has passed since the commitment was made.
/// 4) the _signature hasn't already been used to claim an invite before
* 3) the _signature is signed correctly by the issuer
/// 5) the _signature issuer has not used up all of their invites
* 4) the _signature hasn't already been used to claim an invite before
/// This function doesn't require that the _claimer is calling this function.
* 5) the _signature issuer has not used up all of their invites
/// @param _claimer Address that will be granted the invite.
* This function doesn't require that the _claimer is calling this function.
/// @param _claimableInvite ClaimableInvite struct containing the issuer and nonce.
*
/// @param _signature Signature signed over the claimable invite.
* @param _claimer Address that will be granted the invite.
* @param _claimableInvite ClaimableInvite struct containing the issuer and nonce.
* @param _signature Signature signed over the claimable invite.
*/
function claimInvite(
function claimInvite(
address _claimer,
address _claimer,
ClaimableInvite calldata _claimableInvite,
ClaimableInvite calldata _claimableInvite,
...
...
packages/contracts-bedrock/contracts/periphery/op-nft/libraries/OptimistConstants.sol
View file @
c28e66ab
// SPDX-License-Identifier: MIT
// SPDX-License-Identifier: MIT
pragma solidity 0.8.15;
pragma solidity 0.8.15;
/**
/// @title OptimistConstants
* @title OptimistConstants
/// @notice Library for storing Optimist related constants that are shared in multiple contracts.
* @notice Library for storing Optimist related constants that are shared in multiple contracts.
*/
library OptimistConstants {
library OptimistConstants {
/**
/// @notice Attestation key issued by OptimistInviter allowing the attested account to mint.
* @notice Attestation key issued by OptimistInviter allowing the attested account to mint.
*/
bytes32 internal constant OPTIMIST_CAN_MINT_FROM_INVITE_ATTESTATION_KEY =
bytes32 internal constant OPTIMIST_CAN_MINT_FROM_INVITE_ATTESTATION_KEY =
bytes32("optimist.can-mint-from-invite");
bytes32("optimist.can-mint-from-invite");
}
}
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment