Skip to content

Commit

Permalink
simplified auth method
Browse files Browse the repository at this point in the history
  • Loading branch information
oveddan committed Nov 10, 2023
1 parent 85e0a80 commit 6fd2e27
Show file tree
Hide file tree
Showing 6 changed files with 125 additions and 96 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -234,25 +234,17 @@ library ZoraCreator1155Attribution {
/// @dev copied from ZoraCreator1155Impl
uint256 constant PERMISSION_BIT_MINTER = 2 ** 2;

function isValidSignature(
address originalPremintCreator,
address contractAddress,
bytes32 structHash,
bytes32 hashedVersion,
bytes calldata signature
) internal view returns (bool isValid, address recoveredSigner) {
recoveredSigner = recoverSignerHashed(structHash, signature, contractAddress, hashedVersion, block.chainid);

if (recoveredSigner == address(0)) {
return (false, address(0));
}

// if contract hasn't been created, signer must be the contract admin on the config
function isAuthorizedToCreatePremint(
address signer,
address premintContractConfigContractAdmin,
address contractAddress
) internal view returns (bool authorized) {
// if contract hasn't been created, signer must be the contract admin on the premint config
if (contractAddress.code.length == 0) {
isValid = recoveredSigner == originalPremintCreator;
return signer == premintContractConfigContractAdmin;
} else {
// if contract has been created, signer must have mint new token permission
isValid = IZoraCreator1155(contractAddress).isAdminOrRole(recoveredSigner, CONTRACT_BASE_ID, PERMISSION_BIT_MINTER);
authorized = IZoraCreator1155(contractAddress).isAdminOrRole(signer, CONTRACT_BASE_ID, PERMISSION_BIT_MINTER);
}
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -153,65 +153,42 @@ contract ZoraCreator1155PremintExecutorImpl is
return (true, ERC1155DelegationStorageV1(contractAddress).delegatedTokenId(uid));
}

// @custom:deprecated use isValidSignatureV1 instead
// @custom:deprecated use isAuthorizedToCreatePremint instead
function isValidSignature(
ContractCreationConfig calldata contractConfig,
PremintConfig calldata premintConfig,
bytes calldata signature
) public view returns (bool isValid, address contractAddress, address recoveredSigner) {
contractAddress = getContractAddress(contractConfig);

(isValid, recoveredSigner) = isValidSignatureV1(contractConfig.contractAdmin, contractAddress, premintConfig, signature);
}

/// @notice Recovers the signer of a premint, and checks if the signer is authorized to sign the premint.
/// @dev for use with v1 of premint config, PremintConfig
/// @param premintContractConfigContractAdmin If this contract was created via premint, the original contractConfig.contractAdmin. Otherwise, set to address(0)
/// @param contractAddress The determinstic 1155 contract address the premint is for
/// @param premintConfig The premint config
/// @param signature The signature of the premint
/// @return isValid Whether the signature is valid
/// @return recoveredSigner The signer of the premint
function isValidSignatureV1(
address premintContractConfigContractAdmin,
address contractAddress,
PremintConfig calldata premintConfig,
bytes calldata signature
) public view returns (bool isValid, address recoveredSigner) {
bytes32 hashedPremint = ZoraCreator1155Attribution.hashPremint(premintConfig);

(isValid, recoveredSigner) = ZoraCreator1155Attribution.isValidSignature(
premintContractConfigContractAdmin,
recoveredSigner = ZoraCreator1155Attribution.recoverSignerHashed(
ZoraCreator1155Attribution.hashPremint(premintConfig),
signature,
contractAddress,
hashedPremint,
ZoraCreator1155Attribution.HASHED_VERSION_1,
signature
block.chainid
);

if (recoveredSigner == address(0)) {
return (false, address(0), recoveredSigner);
}

isValid = isAuthorizedToCreatePremint(recoveredSigner, contractConfig.contractAdmin, contractAddress);
}

/// @notice Recovers the signer of a premint, and checks if the signer is authorized to sign the premint.
/// @dev for use with v2 of premint config, PremintConfig
/// @notice Checks if the signer of a premint is authorized to sign a premint for a given contract. If the contract hasn't been created yet,
/// then the signer is authorized if the signer's address matches contractConfig.contractAdmin. Otherwise, the signer must have the PERMISSION_BIT_MINTER
/// role on the contract
/// @param signer The signer of the premint
/// @param premintContractConfigContractAdmin If this contract was created via premint, the original contractConfig.contractAdmin. Otherwise, set to address(0)
/// @param contractAddress The determinstic 1155 contract address the premint is for
/// @param premintConfig The premint config
/// @param signature The signature of the premint
/// @return isValid Whether the signature is valid
/// @return recoveredSigner The signer of the premint
function isValidSignatureV2(
/// @return isAuthorized Whether the signer is authorized
function isAuthorizedToCreatePremint(
address signer,
address premintContractConfigContractAdmin,
address contractAddress,
PremintConfigV2 calldata premintConfig,
bytes calldata signature
) public view returns (bool isValid, address recoveredSigner) {
bytes32 hashedPremint = ZoraCreator1155Attribution.hashPremint(premintConfig);

(isValid, recoveredSigner) = ZoraCreator1155Attribution.isValidSignature(
premintContractConfigContractAdmin,
contractAddress,
hashedPremint,
ZoraCreator1155Attribution.HASHED_VERSION_2,
signature
);
address contractAddress
) public view returns (bool isAuthorized) {
return ZoraCreator1155Attribution.isAuthorizedToCreatePremint(signer, premintContractConfigContractAdmin, contractAddress);
}

/// @notice Returns the versions of the premint signature that the contract supports
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,12 @@ interface ILegacyZoraCreator1155PremintExecutor {
uint256 quantityToMint,
string calldata mintComment
) external payable returns (uint256 newTokenId);

function isAuthorizedToCreatePremint(
address signer,
address premintContractConfigContractAdmin,
address contractAddress
) external view returns (bool isAuthorized);
}

interface IZoraCreator1155PremintExecutorV1 {
Expand All @@ -37,13 +43,6 @@ interface IZoraCreator1155PremintExecutorV1 {
uint256 quantityToMint,
IZoraCreator1155PremintExecutor.MintArguments calldata mintArguments
) external payable returns (IZoraCreator1155PremintExecutor.PremintResult memory);

function isValidSignatureV1(
address originalContractAdmin,
address contractAddress,
PremintConfig calldata premintConfig,
bytes calldata signature
) external view returns (bool isValid, address recoveredSigner);
}

interface IZoraCreator1155PremintExecutorV2 {
Expand All @@ -54,13 +53,6 @@ interface IZoraCreator1155PremintExecutorV2 {
uint256 quantityToMint,
IZoraCreator1155PremintExecutor.MintArguments calldata mintArguments
) external payable returns (IZoraCreator1155PremintExecutor.PremintResult memory);

function isValidSignatureV2(
address originalContractAdmin,
address contractAddress,
PremintConfigV2 calldata premintConfig,
bytes calldata signature
) external view returns (bool isValid, address recoveredSigner);
}

interface IZoraCreator1155PremintExecutor is
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -132,7 +132,7 @@ contract ZoraCreator1155PreminterTest is Test {
assertTrue(isValid);

// now check using new method
(isValid, ) = preminter.isValidSignatureV1(contractConfig.contractAdmin, contractAddress, premintConfig, signature);
isValid = preminter.isAuthorizedToCreatePremint(creator, contractConfig.contractAdmin, contractAddress);
assertTrue(isValid);

// now call the premint function, using the same config that was used to generate the digest, and the signature
Expand Down Expand Up @@ -667,18 +667,13 @@ contract ZoraCreator1155PreminterTest is Test {

address contractAddress = preminter.getContractAddress(contractConfig);

// sign and execute premint
bytes memory signature = _signPremint(contractAddress, premintConfig, creatorPrivateKey, block.chainid);

(bool isValidSignature, address recoveredSigner) = preminter.isValidSignatureV2(
contractConfig.contractAdmin,
contractAddress,
premintConfig,
signature
);
bool isValidSignature = preminter.isAuthorizedToCreatePremint({
signer: creator,
premintContractConfigContractAdmin: contractConfig.contractAdmin,
contractAddress: contractAddress
});

assertEq(creator, recoveredSigner, "recovered the wrong signer");
assertTrue(isValidSignature, "signature should be valid");
assertTrue(isValidSignature, "creator should be allowed to create premint before contract created");

_signAndExecutePremint(contractConfig, premintConfig, creatorPrivateKey, block.chainid, premintExecutor, 1, "hi");

Expand All @@ -694,9 +689,13 @@ contract ZoraCreator1155PreminterTest is Test {
bytes memory newCreatorSignature = _signPremint(contractAddress, premintConfig2, newCreatorPrivateKey, block.chainid);

// it should not be considered a valid signature
(isValidSignature, ) = preminter.isValidSignatureV2(contractConfig.contractAdmin, contractAddress, premintConfig2, newCreatorSignature);
isValidSignature = preminter.isAuthorizedToCreatePremint({
signer: newCreator,
premintContractConfigContractAdmin: contractConfig.contractAdmin,
contractAddress: contractAddress
});

assertFalse(isValidSignature, "signature should not be valid");
assertFalse(isValidSignature, "alternative creator should not be allowed to create a premint");

uint256 quantityToMint = 1;
uint256 mintCost = mintFeeAmount * quantityToMint;
Expand All @@ -712,7 +711,11 @@ contract ZoraCreator1155PreminterTest is Test {
IZoraCreator1155(contractAddress).addPermission(CONTRACT_BASE_ID, newCreator, PERMISSION_BIT_MINTER);

// should now be considered a valid signature
(isValidSignature, ) = preminter.isValidSignatureV2(contractConfig.contractAdmin, contractAddress, premintConfig2, newCreatorSignature);
isValidSignature = preminter.isAuthorizedToCreatePremint({
signer: newCreator,
premintContractConfigContractAdmin: contractConfig.contractAdmin,
contractAddress: contractAddress
});
assertTrue(isValidSignature, "valid signature after granted permission");

vm.deal(premintExecutor, mintCost);
Expand Down
14 changes: 9 additions & 5 deletions packages/premint-sdk/src/preminter.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ import {
ContractCreationConfig,
PremintConfig,
TokenCreationConfig,
isValidSignatureV1,
preminterTypedDataDefinition,
} from "./preminter";

Expand Down Expand Up @@ -191,14 +192,17 @@ describe("ZoraCreator1155Preminter", () => {
});

// recover and verify address is correct
const [,,recoveredAddress] = await publicClient.readContract({
abi: preminterAbi,
address: preminterAddress,
functionName: "isValidSignature",
args: [contractConfig, premintConfig, signedMessage],
const { recoveredAddress, isAuthorized } = await isValidSignatureV1({
contractAddress,
chainId: anvilChainId,
originalContractAdmin: contractConfig.contractAdmin,
premintConfig,
publicClient,
signature: signedMessage
});

expect(recoveredAddress).to.equal(creatorAccount);
expect(isAuthorized).toBe(true);
},

20 * 1000,
Expand Down
65 changes: 63 additions & 2 deletions packages/premint-sdk/src/preminter.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { Address } from "abitype";
import { ExtractAbiFunction, AbiParametersToPrimitiveTypes } from "abitype";
import { zoraCreator1155PremintExecutorImplABI as preminterAbi } from "@zoralabs/protocol-deployments";
import { TypedDataDefinition } from "viem";
import { zoraCreator1155PremintExecutorImplABI as preminterAbi, zoraCreator1155PremintExecutorImplAddress } from "@zoralabs/protocol-deployments";
import { TypedDataDefinition, recoverTypedDataAddress, Hex, PublicClient } from "viem";

type PremintInputs = ExtractAbiFunction<
typeof preminterAbi,
Expand All @@ -14,6 +14,7 @@ export type ContractCreationConfig = PreminterHashDataTypes[0];
export type PremintConfig = PreminterHashDataTypes[1];
export type TokenCreationConfig = PremintConfig["tokenConfig"];


// Convenience method to create the structured typed data
// needed to sign for a premint contract and token
export const preminterTypedDataDefinition = ({
Expand Down Expand Up @@ -72,3 +73,63 @@ export const preminterTypedDataDefinition = ({

return result;
};

export async function isValidSignatureV1({
contractAddress,
originalContractAdmin,
premintConfig,
signature,
chainId,
publicClient
}: {
contractAddress: Address,
originalContractAdmin: Address,
premintConfig: PremintConfig,
signature: Hex,
chainId: number,
publicClient: PublicClient
}): Promise<{
isAuthorized: boolean;
recoveredAddress?: Address;
}> {
const typedData = preminterTypedDataDefinition({
verifyingContract: contractAddress,
premintConfig,
chainId,
});

// recover the address from the signature
let recoveredAddress: Address;

try {
recoveredAddress = await recoverTypedDataAddress({
...typedData,
signature,
});
} catch (error) {
console.error(error);

return {
isAuthorized: false,
}
}

// premint executor is same address on all chains
const premintExecutorAddress = zoraCreator1155PremintExecutorImplAddress[999];

const isAuthorized = await publicClient.readContract({
abi: preminterAbi,
address: premintExecutorAddress,
functionName: 'isAuthorizedToCreatePremint',
args: [
recoveredAddress,
originalContractAdmin,
contractAddress
]
});

return {
isAuthorized,
recoveredAddress
};
}

0 comments on commit 6fd2e27

Please sign in to comment.