Commit 5bb0ade5 authored by James Kim's avatar James Kim

store invite counts in storage instead of on AttestationStation

parent 2bc08bc7
......@@ -97,12 +97,7 @@ contract OptimistInviter_Initializer is Test {
* @notice Returns a user's current invite count, as stored in the AttestationStation.
*/
function _getInviteCount(address _issuer) internal view returns (uint256) {
bytes memory attestation = attestationStation.attestations(
address(optimistInviter),
_issuer,
optimistInviter.CAN_INVITE_ATTESTATION_KEY()
);
return abi.decode(attestation, (uint256));
return optimistInviter.inviteCounts(_issuer);
}
/**
......@@ -226,15 +221,6 @@ contract OptimistInviter_Initializer is Test {
abi.encode(issuer)
);
// OptimistInviter should issue a new attestation with updated invite count
vm.expectEmit(true, true, true, true, address(attestationStation));
emit AttestationCreated(
address(optimistInviter),
issuer,
optimistInviter.CAN_INVITE_ATTESTATION_KEY(),
abi.encode(prevInviteCount - 1)
);
// Should emit an event indicating that the invite was claimed
vm.expectEmit(true, false, false, false, address(optimistInviter));
emit InviteClaimed(issuer, _claimer);
......@@ -267,7 +253,7 @@ contract OptimistInviter_Initializer is Test {
address(optimistInviter),
_to,
optimistInviter.CAN_INVITE_ATTESTATION_KEY(),
abi.encode(3)
bytes("true")
);
vm.prank(alice_inviteGranter);
......@@ -319,7 +305,7 @@ contract OptimistInviterTest is OptimistInviter_Initializer {
address(optimistInviter),
bob,
optimistInviter.CAN_INVITE_ATTESTATION_KEY(),
abi.encode(3)
bytes("true")
);
vm.expectEmit(true, true, true, true, address(attestationStation));
......@@ -327,7 +313,7 @@ contract OptimistInviterTest is OptimistInviter_Initializer {
address(optimistInviter),
sally,
optimistInviter.CAN_INVITE_ATTESTATION_KEY(),
abi.encode(3)
bytes("true")
);
vm.expectEmit(true, true, true, true, address(attestationStation));
......@@ -335,7 +321,7 @@ contract OptimistInviterTest is OptimistInviter_Initializer {
address(optimistInviter),
address(carolERC1271Wallet),
optimistInviter.CAN_INVITE_ATTESTATION_KEY(),
abi.encode(3)
bytes("true")
);
vm.prank(alice_inviteGranter);
......@@ -419,21 +405,14 @@ contract OptimistInviterTest is OptimistInviter_Initializer {
abi.encode(bob)
);
// OptimistInviter should issue a new attestation with updated invite count
vm.expectEmit(true, true, true, true, address(attestationStation));
emit AttestationCreated(
address(optimistInviter),
bob,
optimistInviter.CAN_INVITE_ATTESTATION_KEY(),
abi.encode(2)
);
// Should emit an event indicating that the invite was claimed
vm.expectEmit(true, true, true, true, address(optimistInviter));
emit InviteClaimed(bob, sally);
vm.prank(eve);
optimistInviter.claimInvite(sally, claimableInvite, signature);
assertEq(_getInviteCount(bob), 2);
assertTrue(_hasMintAttestation(sally));
assertFalse(_hasMintAttestation(eve));
}
......@@ -560,17 +539,9 @@ contract OptimistInviterTest is OptimistInviter_Initializer {
abi.encode(address(carolERC1271Wallet))
);
// OptimistInviter should issue a new attestation with updated invite count
vm.expectEmit(true, true, true, true, address(attestationStation));
emit AttestationCreated(
address(optimistInviter),
address(carolERC1271Wallet),
optimistInviter.CAN_INVITE_ATTESTATION_KEY(),
abi.encode(2)
);
vm.prank(sally);
optimistInviter.claimInvite(sally, claimableInvite, signature);
assertEq(_getInviteCount(address(carolERC1271Wallet)), 2);
}
/**
......
......@@ -12,9 +12,9 @@ import {
* @custom:upgradeable
* @title OptimistInviter
* @notice OptimistInviter issues "optimist.can-invite" and "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 invitee uses a claim
* and reveal flow to claim the invite to an address of their choosing.
* 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
* invite to an address of their choosing.
*
* Parties involved:
* 1) INVITE_GRANTER: trusted account that can allow accounts to issue invites
......@@ -23,7 +23,7 @@ import {
*
* 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
* 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
* 3) Off-chain, invite issuer sends the plaintext ClaimableInvite and the signature
* to the recipient
......@@ -50,7 +50,7 @@ contract OptimistInviter is Semver, EIP712Upgradeable {
keccak256("ClaimableInvite(address issuer,bytes32 nonce)");
/**
* @notice Attestation key for granting invites.
* @notice Attestation key for that signals that an account was allowed to issue invites
*/
bytes32 public constant CAN_INVITE_ATTESTATION_KEY = keccak256("optimist.can-invite");
......@@ -95,6 +95,11 @@ contract OptimistInviter is Semver, EIP712Upgradeable {
*/
mapping(address => mapping(bytes32 => bool)) public usedNonces;
/**
* @notice Maps from addresses to number of invites they have.
*/
mapping(address => uint256) public inviteCounts;
/**
* @custom:semver 1.0.0
*
......@@ -140,13 +145,14 @@ contract OptimistInviter is Semver, EIP712Upgradeable {
memory attestations = new AttestationStation.AttestationData[](length);
for (uint256 i; i < length; ) {
// 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
// data field of the attestation.
// Set invite count for account to _inviteCount
inviteCounts[_accounts[i]] = _inviteCount;
// Create an attestation for posterity that the account is allowed to create invites
attestations[i] = AttestationStation.AttestationData({
about: _accounts[i],
key: CAN_INVITE_ATTESTATION_KEY,
val: abi.encode(_inviteCount)
val: bytes("true")
});
unchecked {
......@@ -235,24 +241,16 @@ contract OptimistInviter is Semver, EIP712Upgradeable {
// Set the nonce as used for the issuer so that it cannot be replayed.
usedNonces[_claimableInvite.issuer][_claimableInvite.nonce] = true;
// Check the AttestationStation contract to see how many invites the issuer has left.
bytes memory attestation = ATTESTATION_STATION.attestations(
address(this),
_claimableInvite.issuer,
CAN_INVITE_ATTESTATION_KEY
);
// Failing this check means that the issuer was never granted any invites to begin with.
require(attestation.length > 0, "OptimistInviter: issuer has no invites");
uint256 count = abi.decode(attestation, (uint256));
// Failing this check means that the issuer has used up all of their existing invites.
require(count > 0, "OptimistInviter: issuer has no invites");
require(
inviteCounts[_claimableInvite.issuer] > 0,
"OptimistInviter: issuer has no invites"
);
// Reduce the issuer's invite count by 1 by re-attesting the optimist.can-invite attestation
// with the new count. Can be unchecked because we check that the count is > 0 above.
// Reduce the issuer's invite count by 1. Can be unchecked because we check above that
// count is > 0.
unchecked {
--count;
--inviteCounts[_claimableInvite.issuer];
}
// Create the attestation that the claimer can mint from the issuer's invite.
......@@ -263,12 +261,6 @@ contract OptimistInviter is Semver, EIP712Upgradeable {
abi.encode(_claimableInvite.issuer)
);
ATTESTATION_STATION.attest(
_claimableInvite.issuer,
CAN_INVITE_ATTESTATION_KEY,
abi.encode(count)
);
emit InviteClaimed(_claimableInvite.issuer, _claimer);
}
}
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