Skip to content

Commit

Permalink
Merge remote-tracking branch 'origin/master' into feat/scroll_employe…
Browse files Browse the repository at this point in the history
…e_badge
  • Loading branch information
zimpha committed Oct 15, 2024
2 parents cb609df + 92e08a8 commit ec2fd2e
Show file tree
Hide file tree
Showing 12 changed files with 223 additions and 50 deletions.
17 changes: 16 additions & 1 deletion docs/badges.md
Original file line number Diff line number Diff line change
Expand Up @@ -106,6 +106,7 @@ There are three main badge types of badges:
<th style="text-align: center">Type</th>
<th style="text-align: center">Description</th>
<th style="text-align: center">Requirements</th>
<th style="text-align: center">Required Extensions</th>
<th style="text-align: center">Examples</th>
</tr>

Expand Down Expand Up @@ -163,6 +164,12 @@ There are three main badge types of badges:

<td>

[ScrollBadgeDefaultURI](../src/badge/extensions/ScrollBadgeDefaultURI.sol), [ScrollBadgeEligibilityCheck](../src/badge/extensions/ScrollBadgeEligibilityCheck.sol)

</td>

<td>

[`ScrollBadgePermissionless`](../src/badge/examples/ScrollBadgePermissionless.sol), [`ScrollBadgeTokenOwner`](../src/badge/examples/ScrollBadgeTokenOwner.sol), [`ScrollBadgeWhale`](../src/badge/examples/ScrollBadgeWhale.sol).

</td>
Expand Down Expand Up @@ -214,7 +221,11 @@ There are three main badge types of badges:
</li>
</ul>
</td>
<td>

[ScrollBadgeDefaultURI](../src/badge/extensions/ScrollBadgeDefaultURI.sol), [ScrollBadgeAccessControl](../src/badge/extensions/ScrollBadgeAccessControl.sol)

</td>
<td>

[`EthereumYearBadge`](../src/badge/examples/EthereumYearBadge.sol), [`ScrollBadgeSimple`](../src/badge/examples/ScrollBadgeSimple.sol).
Expand Down Expand Up @@ -250,7 +261,9 @@ There are three main badge types of badges:
</td>

<td>
N/A
</td>

<td>
</td>
</tr>

Expand Down Expand Up @@ -299,6 +312,8 @@ This repo also contains some [examples](src/badge/examples):

We recommend going through the [requirements](#overview-of-requirements) before your badge is published.

Once your badge has been deployed on Scroll, you can auto-check some of these requirements by running `yarn check-badge`.

If your badge minting transaction reverts, we recommend debugging using `cast`:

```sh
Expand Down
4 changes: 3 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,9 @@
"main": "",
"scripts": {
"build": "forge build",
"test": "forge test -vvv"
"test": "forge test -vvv",
"fmt": "forge fmt",
"check-badge": "forge script --rpc-url https://rpc.scroll.io script/CheckBadge.s.sol --skip-simulation"
},
"author": "",
"license": "MIT",
Expand Down
137 changes: 137 additions & 0 deletions script/CheckBadge.s.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,137 @@
// SPDX-License-Identifier: UNLICENSED
pragma solidity 0.8.19;

import {Script} from "forge-std/Script.sol";
import {console} from "forge-std/console.sol";

import {AttesterProxy} from "../src/AttesterProxy.sol";
import {ScrollBadge} from "../src/badge/ScrollBadge.sol";
import {ScrollBadgeEligibilityCheck} from "../src/badge/extensions/ScrollBadgeEligibilityCheck.sol";
import {ScrollBadgeAccessControl} from "../src/badge/extensions/ScrollBadgeAccessControl.sol";

contract CheckBadge is Script {
uint256 SCROLL_CHAIN_ID = 534_352;
address SCROLL_BADGE_RESOLVER_CONTRACT_ADDRESS = 0x4560FECd62B14A463bE44D40fE5Cfd595eEc0113;

function run() external {
address badge = vm.promptAddress("Please provide your badge address");
address attesterProxy = promptAddressOpt("Please provide your attester proxy address (leave empty if none)");
address signer = promptAddressOpt("Please provide your backend signer address (leave empty if none)");

run(badge, attesterProxy, signer);
}

function run(address badge, address attesterProxy, address signer) public {
console.log(
string(
abi.encodePacked(
"Checking badge ",
vm.toString(badge),
" with attester proxy ",
vm.toString(attesterProxy),
" and signer ",
vm.toString(signer)
)
)
);

// check chain id
if (block.chainid != SCROLL_CHAIN_ID) {
revert("Wrong chain, make sure to run this script with --rpc-url https://rpc.scroll.io");
}

// check if badge exists
if (badge.code.length == 0) {
revert(unicode"❌ Badge contract not deployed");
} else {
console.log(unicode"✅ Badge contract deployed");
}

// check if attester proxy exists
if (attesterProxy != address(0) && attesterProxy.code.length == 0) {
revert(unicode"❌ Attester proxy contract not deployed");
} else {
console.log(unicode"✅ Attester proxy contract deployed");
}

// check resolver
try ScrollBadge(badge).resolver() returns (address resolver) {
if (resolver != SCROLL_BADGE_RESOLVER_CONTRACT_ADDRESS) {
console.log(
unicode"❌ Incorrect resolver, make sure that you pass the correct constructor argument to ScrollBadge"
);
} else {
console.log(unicode"✅ Badge resolver configured");
}
} catch {
console.log(unicode"❌ Failed to call badge.resolver(), make sure that your badge implements ScrollBadge");
}

// check default badgeTokenURI
try ScrollBadge(badge).badgeTokenURI(bytes32("")) returns (string memory defaultUri) {
if (bytes(defaultUri).length == 0) {
console.log(
unicode"❌ Missing default badge URI, make sure that your badge implements ScrollBadgeDefaultURI"
);
} else {
console.log(unicode"✅ Default badge URI is configured");
}
} catch {
console.log(
unicode"❌ Missing default badge URI, make sure that your badge implements ScrollBadgeDefaultURI"
);
}

// on-chain eligibility check
if (attesterProxy == address(0)) {
try ScrollBadgeEligibilityCheck(badge).isEligible(address(1)) {
console.log(unicode"✅ On-chain eligibility check is configured");
} catch {
console.log(
unicode"❌ Missing on-chain eligibility check, make sure that your badge implements ScrollBadgeEligibilityCheck"
);
}
}

// authorization
if (attesterProxy != address(0)) {
try ScrollBadgeAccessControl(badge).isAttester(attesterProxy) returns (bool isAttester) {
if (!isAttester) {
console.log(
unicode"❌ Attester proxy is not whitelisted, please call badge.toggleAttester(attesterProxy, true)"
);
} else {
console.log(unicode"✅ Attester proxy is whitelisted");
}
} catch {
console.log(
unicode"❌ Missing access control, make sure that your badge implements ScrollBadgeAccessControl"
);
}
}

if (attesterProxy != address(0) && signer != address(0)) {
try AttesterProxy(attesterProxy).isAttester(signer) returns (bool isAttester) {
if (!isAttester) {
console.log(
unicode"❌ Your signer is not whitelisted, please call attesterProxy.toggleAttester(signer, true)"
);
} else {
console.log(unicode"✅ Signer is whitelisted");
}
} catch {
console.log(
unicode"❌ Failed to query attester proxy, make sure this contract is an instance of AttesterProxy"
);
}
}
}

function promptAddressOpt(string memory promptText) private returns (address addr) {
string memory str = vm.prompt(promptText);

if (bytes(str).length > 0) {
addr = vm.parseAddress(str);
}
}
}
2 changes: 1 addition & 1 deletion script/DeployCanvasTestBadgeContracts.sol
Original file line number Diff line number Diff line change
Expand Up @@ -84,7 +84,7 @@ contract DeployCanvasTestBadgeContracts is Script {
tokens[0] = 0xDd7d857F570B0C211abfe05cd914A85BefEC2464;
}

ScrollBadgeTokenOwner badge4 = new ScrollBadgeTokenOwner(address(resolver), tokens);
ScrollBadgeTokenOwner badge4 = new ScrollBadgeTokenOwner(address(resolver), "", tokens);

// deploy Ethereum year badge
EthereumYearBadge badge5 = new EthereumYearBadge(address(resolver), "https://nft.scroll.io/canvas/year/");
Expand Down
18 changes: 12 additions & 6 deletions src/badge/examples/EthereumYearBadge.sol
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import {Strings} from "@openzeppelin/contracts/utils/Strings.sol";
import {ScrollBadge} from "../ScrollBadge.sol";
import {ScrollBadgeAccessControl} from "../extensions/ScrollBadgeAccessControl.sol";
import {ScrollBadgeCustomPayload} from "../extensions/ScrollBadgeCustomPayload.sol";
import {ScrollBadgeDefaultURI} from "../extensions/ScrollBadgeDefaultURI.sol";
import {ScrollBadgeNoExpiry} from "../extensions/ScrollBadgeNoExpiry.sol";
import {ScrollBadgeNonRevocable} from "../extensions/ScrollBadgeNonRevocable.sol";
import {ScrollBadgeSingleton} from "../extensions/ScrollBadgeSingleton.sol";
Expand All @@ -24,27 +25,32 @@ function decodePayloadData(bytes memory data) pure returns (uint256) {
contract EthereumYearBadge is
ScrollBadgeAccessControl,
ScrollBadgeCustomPayload,
ScrollBadgeDefaultURI,
ScrollBadgeNoExpiry,
ScrollBadgeNonRevocable,
ScrollBadgeSingleton
{
/// @notice The base token URI.
string public baseTokenURI;

constructor(address resolver_, string memory baseTokenURI_) ScrollBadge(resolver_) {
baseTokenURI = baseTokenURI_;
constructor(address resolver_, string memory baseTokenURI_)
ScrollBadge(resolver_)
ScrollBadgeDefaultURI(baseTokenURI_)
{
// empty
}

/// @notice Update the base token URI.
/// @param baseTokenURI_ The new base token URI.
function updateBaseTokenURI(string memory baseTokenURI_) external onlyOwner {
baseTokenURI = baseTokenURI_;
defaultBadgeURI = baseTokenURI_;
}

/// @inheritdoc ScrollBadge
function onIssueBadge(Attestation calldata attestation)
internal
override (
ScrollBadge,
ScrollBadgeAccessControl,
ScrollBadgeCustomPayload,
ScrollBadgeNoExpiry,
Expand All @@ -67,13 +73,13 @@ contract EthereumYearBadge is
return super.onRevokeBadge(attestation);
}

/// @inheritdoc ScrollBadge
function badgeTokenURI(bytes32 uid) public view override returns (string memory) {
/// @inheritdoc ScrollBadgeDefaultURI
function getBadgeTokenURI(bytes32 uid) internal view override returns (string memory) {
Attestation memory attestation = getAndValidateBadge(uid);
bytes memory payload = getPayload(attestation);
uint256 year = decodePayloadData(payload);

return string(abi.encodePacked(baseTokenURI, Strings.toString(year), ".json"));
return string(abi.encodePacked(defaultBadgeURI, Strings.toString(year), ".json"));
}

/// @inheritdoc ScrollBadgeCustomPayload
Expand Down
20 changes: 12 additions & 8 deletions src/badge/examples/ScrollBadgePermissionless.sol
Original file line number Diff line number Diff line change
Expand Up @@ -5,14 +5,23 @@ pragma solidity 0.8.19;
import {Attestation} from "@eas/contracts/IEAS.sol";

import {ScrollBadge} from "../ScrollBadge.sol";
import {ScrollBadgeSelfAttest} from "../extensions/ScrollBadgeSelfAttest.sol";
import {ScrollBadgeDefaultURI} from "../extensions/ScrollBadgeDefaultURI.sol";
import {ScrollBadgeEligibilityCheck} from "../extensions/ScrollBadgeEligibilityCheck.sol";
import {ScrollBadgeSelfAttest} from "../extensions/ScrollBadgeSelfAttest.sol";
import {ScrollBadgeSingleton} from "../extensions/ScrollBadgeSingleton.sol";

/// @title ScrollBadgePermissionless
/// @notice A simple badge that anyone can mint in a permissionless manner.
contract ScrollBadgePermissionless is ScrollBadgeSelfAttest, ScrollBadgeEligibilityCheck, ScrollBadgeSingleton {
constructor(address resolver_) ScrollBadge(resolver_) {
contract ScrollBadgePermissionless is
ScrollBadgeDefaultURI,
ScrollBadgeEligibilityCheck,
ScrollBadgeSelfAttest,
ScrollBadgeSingleton
{
constructor(address resolver_, string memory _defaultBadgeURI)
ScrollBadge(resolver_)
ScrollBadgeDefaultURI(_defaultBadgeURI)
{
// empty
}

Expand All @@ -35,9 +44,4 @@ contract ScrollBadgePermissionless is ScrollBadgeSelfAttest, ScrollBadgeEligibil
{
return super.onRevokeBadge(attestation);
}

/// @inheritdoc ScrollBadge
function badgeTokenURI(bytes32 /*uid*/ ) public pure override returns (string memory) {
return "";
}
}
20 changes: 7 additions & 13 deletions src/badge/examples/ScrollBadgeSimple.sol
Original file line number Diff line number Diff line change
Expand Up @@ -4,23 +4,22 @@ pragma solidity 0.8.19;

import {Attestation} from "@eas/contracts/IEAS.sol";

import {ScrollBadge} from "../ScrollBadge.sol";
import {ScrollBadgeAccessControl} from "../extensions/ScrollBadgeAccessControl.sol";
import {ScrollBadgeDefaultURI} from "../extensions/ScrollBadgeDefaultURI.sol";
import {ScrollBadgeSingleton} from "../extensions/ScrollBadgeSingleton.sol";
import {ScrollBadge} from "../ScrollBadge.sol";

/// @title ScrollBadgeSimple
/// @notice A simple badge that has the same static metadata for each token.
contract ScrollBadgeSimple is ScrollBadgeAccessControl, ScrollBadgeSingleton {
string public sharedTokenURI;

constructor(address resolver_, string memory tokenUri_) ScrollBadge(resolver_) {
sharedTokenURI = tokenUri_;
contract ScrollBadgeSimple is ScrollBadgeAccessControl, ScrollBadgeDefaultURI, ScrollBadgeSingleton {
constructor(address resolver_, string memory tokenUri_) ScrollBadge(resolver_) ScrollBadgeDefaultURI(tokenUri_) {
// empty
}

/// @inheritdoc ScrollBadge
function onIssueBadge(Attestation calldata attestation)
internal
override (ScrollBadgeAccessControl, ScrollBadgeSingleton)
override (ScrollBadge, ScrollBadgeAccessControl, ScrollBadgeSingleton)
returns (bool)
{
return super.onIssueBadge(attestation);
Expand All @@ -29,14 +28,9 @@ contract ScrollBadgeSimple is ScrollBadgeAccessControl, ScrollBadgeSingleton {
/// @inheritdoc ScrollBadge
function onRevokeBadge(Attestation calldata attestation)
internal
override (ScrollBadgeAccessControl, ScrollBadgeSingleton)
override (ScrollBadge, ScrollBadgeAccessControl, ScrollBadgeSingleton)
returns (bool)
{
return super.onRevokeBadge(attestation);
}

/// @inheritdoc ScrollBadge
function badgeTokenURI(bytes32 /*uid*/ ) public view override returns (string memory) {
return sharedTokenURI;
}
}
Loading

0 comments on commit ec2fd2e

Please sign in to comment.