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
b025a7b9
Commit
b025a7b9
authored
Mar 27, 2023
by
James Kim
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
update comments and make one call to AttestationStation in bulk
parent
36c95f4b
Changes
2
Hide whitespace changes
Inline
Side-by-side
Showing
2 changed files
with
74 additions
and
32 deletions
+74
-32
OptimistInviter.t.sol
...s-periphery/contracts/foundry-tests/OptimistInviter.t.sol
+13
-15
OptimistInviter.sol
...-periphery/contracts/universal/op-nft/OptimistInviter.sol
+61
-17
No files found.
packages/contracts-periphery/contracts/foundry-tests/OptimistInviter.t.sol
View file @
b025a7b9
...
@@ -20,7 +20,6 @@ contract OptimistInviter_Initializer is Test {
...
@@ -20,7 +20,6 @@ contract OptimistInviter_Initializer is Test {
bytes val
bytes val
);
);
bytes32 CLAIMABLE_INVITE_TYPEHASH;
bytes32 EIP712_DOMAIN_TYPEHASH;
bytes32 EIP712_DOMAIN_TYPEHASH;
address internal alice_inviteGranter;
address internal alice_inviteGranter;
...
@@ -63,7 +62,6 @@ contract OptimistInviter_Initializer is Test {
...
@@ -63,7 +62,6 @@ contract OptimistInviter_Initializer is Test {
vm.deal(ted, 1 ether);
vm.deal(ted, 1 ether);
vm.deal(eve, 1 ether);
vm.deal(eve, 1 ether);
CLAIMABLE_INVITE_TYPEHASH = keccak256("ClaimableInvite(address issuer,bytes32 nonce)");
EIP712_DOMAIN_TYPEHASH = keccak256(
EIP712_DOMAIN_TYPEHASH = keccak256(
"EIP712Domain(string name,string version,uint256 chainId,address verifyingContract)"
"EIP712Domain(string name,string version,uint256 chainId,address verifyingContract)"
);
);
...
@@ -99,7 +97,7 @@ contract OptimistInviter_Initializer is Test {
...
@@ -99,7 +97,7 @@ contract OptimistInviter_Initializer is Test {
bytes memory attestation = attestationStation.attestations(
bytes memory attestation = attestationStation.attestations(
address(optimistInviter),
address(optimistInviter),
_issuer,
_issuer,
bytes32("optimist.can-invite"
)
optimistInviter.CAN_INVITE_ATTESTATION_KEY(
)
);
);
return abi.decode(attestation, (uint256));
return abi.decode(attestation, (uint256));
}
}
...
@@ -111,7 +109,7 @@ contract OptimistInviter_Initializer is Test {
...
@@ -111,7 +109,7 @@ contract OptimistInviter_Initializer is Test {
bytes memory attestation = attestationStation.attestations(
bytes memory attestation = attestationStation.attestations(
address(optimistInviter),
address(optimistInviter),
_claimer,
_claimer,
bytes32("optimist.can-mint-from-invite"
)
optimistInviter.CAN_MINT_FROM_INVITE_ATTESTATION_KEY(
)
);
);
return attestation.length > 0;
return attestation.length > 0;
}
}
...
@@ -221,7 +219,7 @@ contract OptimistInviter_Initializer is Test {
...
@@ -221,7 +219,7 @@ contract OptimistInviter_Initializer is Test {
emit AttestationCreated(
emit AttestationCreated(
address(optimistInviter),
address(optimistInviter),
_claimer,
_claimer,
bytes32("optimist.can-mint-from-invite"
),
optimistInviter.CAN_MINT_FROM_INVITE_ATTESTATION_KEY(
),
abi.encode(issuer)
abi.encode(issuer)
);
);
...
@@ -230,7 +228,7 @@ contract OptimistInviter_Initializer is Test {
...
@@ -230,7 +228,7 @@ contract OptimistInviter_Initializer is Test {
emit AttestationCreated(
emit AttestationCreated(
address(optimistInviter),
address(optimistInviter),
issuer,
issuer,
bytes32("optimist.can-invite"
),
optimistInviter.CAN_INVITE_ATTESTATION_KEY(
),
abi.encode(prevInviteCount - 1)
abi.encode(prevInviteCount - 1)
);
);
...
@@ -265,7 +263,7 @@ contract OptimistInviter_Initializer is Test {
...
@@ -265,7 +263,7 @@ contract OptimistInviter_Initializer is Test {
emit AttestationCreated(
emit AttestationCreated(
address(optimistInviter),
address(optimistInviter),
_to,
_to,
bytes32("optimist.can-invite"
),
optimistInviter.CAN_INVITE_ATTESTATION_KEY(
),
abi.encode(3)
abi.encode(3)
);
);
...
@@ -286,7 +284,7 @@ contract OptimistInviter_Initializer is Test {
...
@@ -286,7 +284,7 @@ contract OptimistInviter_Initializer is Test {
return
return
keccak256(
keccak256(
abi.encode(
abi.encode(
CLAIMABLE_INVITE_TYPEHASH
,
optimistInviter.CLAIMABLE_INVITE_TYPEHASH()
,
_claimableInvite.issuer,
_claimableInvite.issuer,
_claimableInvite.nonce
_claimableInvite.nonce
)
)
...
@@ -341,7 +339,7 @@ contract OptimistInviterTest is OptimistInviter_Initializer {
...
@@ -341,7 +339,7 @@ contract OptimistInviterTest is OptimistInviter_Initializer {
emit AttestationCreated(
emit AttestationCreated(
address(optimistInviter),
address(optimistInviter),
bob,
bob,
bytes32("optimist.can-invite"
),
optimistInviter.CAN_INVITE_ATTESTATION_KEY(
),
abi.encode(3)
abi.encode(3)
);
);
...
@@ -349,7 +347,7 @@ contract OptimistInviterTest is OptimistInviter_Initializer {
...
@@ -349,7 +347,7 @@ contract OptimistInviterTest is OptimistInviter_Initializer {
emit AttestationCreated(
emit AttestationCreated(
address(optimistInviter),
address(optimistInviter),
sally,
sally,
bytes32("optimist.can-invite"
),
optimistInviter.CAN_INVITE_ATTESTATION_KEY(
),
abi.encode(3)
abi.encode(3)
);
);
...
@@ -357,7 +355,7 @@ contract OptimistInviterTest is OptimistInviter_Initializer {
...
@@ -357,7 +355,7 @@ contract OptimistInviterTest is OptimistInviter_Initializer {
emit AttestationCreated(
emit AttestationCreated(
address(optimistInviter),
address(optimistInviter),
address(carolERC1271Wallet),
address(carolERC1271Wallet),
bytes32("optimist.can-invite"
),
optimistInviter.CAN_INVITE_ATTESTATION_KEY(
),
abi.encode(3)
abi.encode(3)
);
);
...
@@ -438,7 +436,7 @@ contract OptimistInviterTest is OptimistInviter_Initializer {
...
@@ -438,7 +436,7 @@ contract OptimistInviterTest is OptimistInviter_Initializer {
emit AttestationCreated(
emit AttestationCreated(
address(optimistInviter),
address(optimistInviter),
sally,
sally,
bytes32("optimist.can-mint-from-invite"
),
optimistInviter.CAN_MINT_FROM_INVITE_ATTESTATION_KEY(
),
abi.encode(bob)
abi.encode(bob)
);
);
...
@@ -447,7 +445,7 @@ contract OptimistInviterTest is OptimistInviter_Initializer {
...
@@ -447,7 +445,7 @@ contract OptimistInviterTest is OptimistInviter_Initializer {
emit AttestationCreated(
emit AttestationCreated(
address(optimistInviter),
address(optimistInviter),
bob,
bob,
bytes32("optimist.can-invite"
),
optimistInviter.CAN_INVITE_ATTESTATION_KEY(
),
abi.encode(2)
abi.encode(2)
);
);
...
@@ -588,7 +586,7 @@ contract OptimistInviterTest is OptimistInviter_Initializer {
...
@@ -588,7 +586,7 @@ contract OptimistInviterTest is OptimistInviter_Initializer {
emit AttestationCreated(
emit AttestationCreated(
address(optimistInviter),
address(optimistInviter),
sally,
sally,
bytes32("optimist.can-mint-from-invite"
),
optimistInviter.CAN_MINT_FROM_INVITE_ATTESTATION_KEY(
),
abi.encode(address(carolERC1271Wallet))
abi.encode(address(carolERC1271Wallet))
);
);
...
@@ -597,7 +595,7 @@ contract OptimistInviterTest is OptimistInviter_Initializer {
...
@@ -597,7 +595,7 @@ contract OptimistInviterTest is OptimistInviter_Initializer {
emit AttestationCreated(
emit AttestationCreated(
address(optimistInviter),
address(optimistInviter),
address(carolERC1271Wallet),
address(carolERC1271Wallet),
bytes32("optimist.can-invite"
),
optimistInviter.CAN_INVITE_ATTESTATION_KEY(
),
abi.encode(2)
abi.encode(2)
);
);
...
...
packages/contracts-periphery/contracts/universal/op-nft/OptimistInviter.sol
View file @
b025a7b9
...
@@ -15,6 +15,24 @@ import {
...
@@ -15,6 +15,24 @@ import {
* "optimist.can-mint-from-invite" attestations. Accounts that have a "optimist.can-invite"
* "optimist.can-mint-from-invite" attestations. Accounts that have a "optimist.can-invite"
* attestation can issue signatures that allow other accounts to claim an invite. The
* attestation can issue signatures that allow other accounts to claim an invite. The
* invitee uses a claim and reveal flow to claim the invite to an address of their choosing.
* invitee uses a claim and reveal flow to claim the invite to an address of their choosing.
*
* Parties involved:
* 1) INVITE_GRANTER: trusted account that can allow accounts to issue invites
* 2) issuer: account that is allowed to issue invites
* 3) claimer: account that receives the invites
*
* Flow:
* 1) INVITE_GRANTER calls _setInviteCount to allow an issuer to issue a certain number
* of invites, creating "optimist.can-invite" attestations for the issuer
* 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
* to the recipient
* 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
received signature [keccak256(abi.encode(addressToReceiveTo, receivedSignature))]
* using the commitInvite function
* 6) claimer reveals the plaintext ClaimableInvite and the signature using the
* claimInvite function, receiving the "optimist.can-mint-from-invite" attestation
*/
*/
contract OptimistInviter is Semver, EIP712Upgradeable {
contract OptimistInviter is Semver, EIP712Upgradeable {
/**
/**
...
@@ -27,24 +45,20 @@ contract OptimistInviter is Semver, EIP712Upgradeable {
...
@@ -27,24 +45,20 @@ contract OptimistInviter is Semver, EIP712Upgradeable {
/**
/**
* @notice EIP712 typehash for the ClaimableInvite type.
* @notice EIP712 typehash for the ClaimableInvite type.
* keccak256("ClaimableInvite(address issuer,bytes32 nonce)")
*/
*/
bytes32 public constant CLAIMABLE_INVITE_TYPEHASH =
bytes32 public constant CLAIMABLE_INVITE_TYPEHASH =
0x6529fd129351e725d7bcbc468b0b0b4675477e56b58514e69ab7e66ddfd20fce
;
keccak256("ClaimableInvite(address issuer,bytes32 nonce)")
;
/**
/**
* @notice Attestation key for granting invites.
* @notice Attestation key for granting invites.
* bytes32("optimist.can-invite")
*/
*/
bytes32 public constant CAN_INVITE_ATTESTATION_KEY =
bytes32 public constant CAN_INVITE_ATTESTATION_KEY = bytes32("optimist.can-invite");
0x6f7074696d6973742e63616e2d696e7669746500000000000000000000000000;
/**
/**
* @notice Attestation key allowing the attested account to mint.
* @notice Attestation key allowing the attested account to mint.
* bytes32("optimist.can-mint-from-invite")
*/
*/
bytes32 public constant CAN_MINT_FROM_INVITE_ATTESTATION_KEY =
bytes32 public constant CAN_MINT_FROM_INVITE_ATTESTATION_KEY =
0x6f7074696d6973742e63616e2d6d696e742d66726f6d2d696e76697465000000
;
bytes32("optimist.can-mint-from-invite")
;
/**
/**
* @notice Granter who can set accounts' invite counts.
* @notice Granter who can set accounts' invite counts.
...
@@ -116,25 +130,48 @@ contract OptimistInviter is Semver, EIP712Upgradeable {
...
@@ -116,25 +130,48 @@ contract OptimistInviter is Semver, EIP712Upgradeable {
uint256 length = _accounts.length;
uint256 length = _accounts.length;
AttestationStation.AttestationData[]
memory attestations = new AttestationStation.AttestationData[](length);
for (uint256 i; i < length; ) {
for (uint256 i; i < length; ) {
// The granted invites are stored as an attestation from this contract on the
// The granted invites are stored as an attestation from this contract on the
// AttestationStation contract. Number of invites is stored as a encoded uint256 in the
// AttestationStation contract. Number of invites is stored as a encoded uint256 in the
// data field of the attestation.
// data field of the attestation.
ATTESTATION_STATION.attest(
attestations[i] = AttestationStation.AttestationData({
_accounts[i],
about:
_accounts[i],
CAN_INVITE_ATTESTATION_KEY,
key:
CAN_INVITE_ATTESTATION_KEY,
abi.encode(_inviteCount)
val:
abi.encode(_inviteCount)
);
}
);
unchecked {
unchecked {
++i;
++i;
}
}
}
}
ATTESTATION_STATION.attest(attestations);
}
function attest(AttestationStation.AttestationData[] calldata _attestations) public {
// Only invite granter can grant invites
require(
msg.sender == INVITE_GRANTER,
"OptimistInviter: only invite granter can issue attestations"
);
ATTESTATION_STATION.attest(_attestations);
}
}
/**
/**
* @notice Allows anyone to commit a received signature along with the address to claim to.
* @notice Allows anyone (but likely the claimer) to commit a received signature along with the
* This is necessary to prevent front-running when the invitee is claiming the invite.
* address to claim to.
*
* 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
* 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
* 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.
*
*
*
* @param _commitment A hash of the claimer and signature concatenated.
* @param _commitment A hash of the claimer and signature concatenated.
* keccak256(abi.encode(_claimer, _signature))
* keccak256(abi.encode(_claimer, _signature))
...
@@ -145,8 +182,15 @@ contract OptimistInviter is Semver, EIP712Upgradeable {
...
@@ -145,8 +182,15 @@ contract OptimistInviter is Semver, EIP712Upgradeable {
/**
/**
* @notice Allows anyone to reveal a commitment and claim an invite.
* @notice Allows anyone to reveal a commitment and claim an invite.
* The claimer ++ signature pair should have been previously committed using
*
* commitInvite. Doesn't require that the claimer is calling this function.
* The hash, keccak256(abi.encode(_claimer, _signature)), should have been already
* committed using commitInvite. Before issuing the "optimist.can-mint-from-invite"
* attestation, this function checks that
* 1) the hash corresponding to the _claimer and the _signature was committed
* 2) the _signature is signed correctly by the issuer
* 3) the _signature hasn't already been used to claim an invite before
* 4) the _signature issuer has not used up all of their invites
* This function doesn't require that the _claimer is calling this function.
*
*
* @param _claimer Address that will be granted the invite.
* @param _claimer Address that will be granted the invite.
* @param _claimableInvite ClaimableInvite struct containing the issuer and nonce.
* @param _claimableInvite ClaimableInvite struct containing the issuer and nonce.
...
@@ -218,7 +262,7 @@ contract OptimistInviter is Semver, EIP712Upgradeable {
...
@@ -218,7 +262,7 @@ contract OptimistInviter is Semver, EIP712Upgradeable {
);
);
// Reduce the issuer's invite count by 1 by re-attesting the optimist.can-invite attestation
// Reduce the issuer's invite count by 1 by re-attesting the optimist.can-invite attestation
// with the new count.
// with the new count.
Can be unchecked because we check that the count is > 0 above.
unchecked {
unchecked {
--count;
--count;
}
}
...
...
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