Skip to content

Commit

Permalink
fix tests
Browse files Browse the repository at this point in the history
  • Loading branch information
oveddan committed Nov 15, 2023
1 parent 48830c3 commit 014925f
Show file tree
Hide file tree
Showing 10 changed files with 800 additions and 101 deletions.
13 changes: 11 additions & 2 deletions .changeset/violet-starfishes-visit.md
Original file line number Diff line number Diff line change
Expand Up @@ -109,6 +109,15 @@ struct TokenCreationConfig {
* new function `premintV1` - takes a `PremintConfig`, and premint v1 signature, and executes a premint, with added functionality of being able to specify mint referral and mint recipient
* new function `premintV2` - takes a `PremintConfigV2` signature and executes a premint, with being able to specify mint referral and mint recipient
* deprecated function `premint` - call `premintV1` instead
* new function `isValidSignatureV1` - takes an 1155 address, contract admin, premint v1 config and signature, and validates the signature. Can be used for 1155 contracts that were not created via the premint executor contract.
* new function `isValidSignatureV2` - takes an 1155 address, contract admin, premint v2 config and signature, and validates the signature. Can be used for 1155 contracts that were not created via the premint executor contract.
* new function

```solidity
isAuthorizedToCreatePremint(
address signer,
address premintContractConfigContractAdmin,
address contractAddress
) public view returns (bool isAuthorized)
```

takes a signer, contractConfig.contractAdmin, and 1155 address, and determines if the signer is authorized to sign premints on the given contract. Replaces `isValidSignature` - by putting the burden on clients to first decode the signature, then pass the recovered signer to this function to determine if the signer has premint authorization on the contract.
* deprecated function `isValidSignature` - call `isValidSignatureV1` instead
5 changes: 5 additions & 0 deletions .changeset/wicked-dolphins-remain.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@zoralabs/premint-sdk": patch
---

`preminter` exposes new function isValidSignatureV1 that recovers a signer from a signed premint and determines if that signer is authorized to sign
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
20 changes: 12 additions & 8 deletions packages/protocol-sdk/src/premint/preminter.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ import {
PremintConfig,
TokenCreationConfig,
preminterTypedDataDefinition,
isValidSignatureV1,
} from "./preminter";
import {
AnvilViemClientsTest,
Expand Down Expand Up @@ -182,15 +183,18 @@ describe("ZoraCreator1155Preminter", () => {
account: creatorAccount,
});

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

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

expect(recoveredAddress).to.equal(creatorAccount);
},
Expand Down
70 changes: 68 additions & 2 deletions packages/protocol-sdk/src/premint/preminter.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,15 @@
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 Down Expand Up @@ -68,5 +76,63 @@ export const preminterTypedDataDefinition = ({
primaryType: "CreatorAttribution",
};

// console.log({ result, deleted });

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,
};
}
Loading

0 comments on commit 014925f

Please sign in to comment.