When creating a new application, the first thing you’ll do is create a Patchwork Scope for your app. You’ll then define within that scope which contracts can mint, assign NFTs, and modify metadata. Scopes are free to claim on a first-come-first-serve basis, and can be transferred to other parties.

Scope creation

Scopes are claimed using PatchworkProtocol:claimScope(). Typically you’ll call this in your contracts’ deploy script, but it can be manually called if desired.

When called, the owner of the claimed Scope is set to the party calling the function.

For example, this Forge deploy script claims the Scope fooscope123.

deploy.s.sol
pragma solidity ^0.8.13;

import "forge-std/Script.sol";
import "@patchwork/PatchworkProtocol.sol";

contract FooDeploy is Script {
    function run() external {
        PatchworkProtocol patchworkProtocol = PatchworkProtocol(ADDRESSFORPATCHWORK);

        vm.startBroadcast();
        patchworkProtocol.claimScope("fooscope123");

        // ...rest of deploy code
    }
}

Scope permissions

Operators

Scope Operators are added and removed by the scope owner.

Operators may perform the following functions:

  • Add/Remove bankers
  • Add/Remove whitelisted addresses
  • Configure minting
  • Set patch and assignment fees

It is common to have an EOA or a contract act as an operator. Contracts as operators are often used to have the contract perform logic and run assignment routines.

Bankers

Scope Bankers are added and removed by scope owners or operators.

Bankers may perform the following functions:

  • Withdraw fees collected by the protocol

Whitelist

Whitelisting is how scopes prevent spoofing. Whitelisting can be disabled for a scope for cases where spoofing doesn’t matter, but by defualt an address must be whitelisted in order to claim it is part of the scope.

Adding contracts to your Scope

Registering a contract to your Scope is a 2-part process. The first part is declaring the Scope in your contract’s Patchwork721 constructor, where the first param is the name of your claimed Scope.

Foo.sol
pragma solidity ^0.8.23;

import "@patchwork/Patchwork721.sol";

contract Foo is Patchwork721 {
    constructor(address manager_, address owner_)
        Patchwork721("fooscope123", "Foo", "FOO", manager_, owner_)
    { }

    // ...rest of contract code
}

The second part is granting your deployed contract the relevant Scope permissions. This is typically done in your contract’s deploy script.

deploy.s.sol
    // Sets scope rules to default: No user patch, No user assign, Require whitelist
    patchworkProtocol.setScopeRules("fooscope123", false, false, true);
    // Add foocontract as a scope operator so it may perform assignments
    patchworkProtocol.addOperator("fooscope123", address(foocontract));
    // Whitelist foocontract in the scope
    patchworkProtocol.addWhitelist("fooscope123", address(foocontract));

Adding and removing operators

    patchworkProtocol.addOperator("fooscope123", opAddr)
    patchworkProtocol.removeOperator("fooscope123", opAddr)

Transfering Scopes

Scope transfers must first elect a new owner by calling transferScopeOwner from the current owner’s address, then the new owner must call acceptScopeTransfer for the transfer to execute. To cancel a transfer before the scope has been accepted, call cancelScopeTransfer

    patchworkProtocol.transferScopeOwnership("fooscope123", newOwnerAddr);
    patchworkProtocol.cancelScopeTransfer("fooscope123")
    patchworkProtocol.acceptScopeTransfer("fooscope123")
    patchworkProtocol.getScopeOwnerElect("fooscope123")
    patchworkProtocol.getScopeOwner("fooscope123")

Adding and removing bankers

    patchworkProtocol.addBanker("fooscope123", bankerAddr)
    patchworkProtocol.removeBanker("fooscope123", bankerAddr)

Withdrawing fees

    uint256 balance = patchworkProtocol.balanceOf("fooscope123");
    patchworkProtocol.withdraw("fooscope123", balance);

Configuring minting

    patchworkProtocol.setMintConfiguration(mintableAddr, config)

Setting patch and assignment fees

    patchworkProtocol.setPatchFee(patchContractAddr, baseFee)
    patchworkProtocol.getPatchFee(patchContractAddr)
    patchworkProtocol.setAssignFee(fragmentContractAddr, baseFee)
    patchworkProtocol.getAssignFee(fragmentContractAddr)