[TS SDK] Feature: Accept a string for signature mint UID (#4270)

## Problem solved

Auto-encodes strings passed to `generateSignatureMint` across all token types. This change could cause confusion since the response uid from the contract will still be in hex form.

<!-- start pr-codex -->

---

## PR-Codex overview
This PR allows accepting string UIDs for signature minting in ERC721 and ERC20 contracts.

### Detailed summary
- Updated functions to accept and encode string UIDs for minting signatures
- Added tests for generating mint signatures with custom string UIDs
- Updated dependencies and peer dependencies in package.json

> ✨ Ask PR-Codex anything about this PR by commenting with `/codex {your question}`

<!-- end pr-codex -->
This commit is contained in:
gregfromstl
2024-09-01 19:11:35 +00:00
parent a2f2c7e172
commit 5475551e5d
8 changed files with 141 additions and 11 deletions
+5
View File
@@ -0,0 +1,5 @@
---
"thirdweb": patch
---
Allow accepting a string for signature mint UIDs
+4 -1
View File
@@ -23,7 +23,10 @@
},
"./package.json": "./package.json"
},
"files": ["dist/*", "src/*"],
"files": [
"dist/*",
"src/*"
],
"dependencies": {
"@aws-sdk/client-lambda": "3.577.0",
"@aws-sdk/credential-providers": "3.577.0",
@@ -8,7 +8,7 @@ import type { ThirdwebContract } from "../../../contract/contract.js";
import type { BaseTransactionOptions } from "../../../transaction/types.js";
import { toBigInt } from "../../../utils/bigint.js";
import { dateToSeconds, tenYearsFromNow } from "../../../utils/date.js";
import type { Hex } from "../../../utils/encoding/hex.js";
import { type Hex, isHex, stringToHex } from "../../../utils/encoding/hex.js";
import type { NFTInput } from "../../../utils/nft/parseNft.js";
import { randomBytesHex } from "../../../utils/random.js";
import type { Account } from "../../../wallets/interfaces/wallet.js";
@@ -135,7 +135,14 @@ export async function generateMintSignature(
return "";
})(),
// uid computation
mintRequest.uid || (await randomBytesHex()),
((): Hex => {
if (mintRequest.uid) {
return isHex(mintRequest.uid)
? mintRequest.uid
: stringToHex(mintRequest.uid, { size: 32 });
}
return randomBytesHex();
})(),
]);
const startTime = mintRequest.validityStartTimestamp || new Date(0);
@@ -190,7 +197,7 @@ type GeneratePayloadInput = {
currency?: Address;
validityStartTimestamp?: Date;
validityEndTimestamp?: Date;
uid?: Hex;
uid?: string;
} & (
| {
metadata: NFTInput | string;
@@ -194,4 +194,38 @@ describe.runIf(process.env.TW_SECRET_KEY)("generateMintSignature1155", () => {
expect(payload.uid).toBe(uid);
expect(signature.length).toBe(132);
});
it("should generate a mint signature with custom values", async () => {
const { payload, signature } = await generateMintSignature({
mintRequest: {
to: TEST_ACCOUNT_B.address,
quantity: 10n,
royaltyRecipient: TEST_ACCOUNT_B.address,
royaltyBps: 500,
primarySaleRecipient: TEST_ACCOUNT_A.address,
tokenId: 0n,
pricePerToken: "0.2",
currency: erc20TokenContract.address,
validityStartTimestamp: new Date(1635724800),
validityEndTimestamp: new Date(1867260800),
uid: "abcdef1234567890",
},
account: TEST_ACCOUNT_A,
contract: erc1155Contract,
});
expect(payload.to).toBe(TEST_ACCOUNT_B.address);
expect(payload.tokenId).toBe(0n);
expect(payload.royaltyRecipient).toBe(TEST_ACCOUNT_B.address);
expect(payload.royaltyBps).toBe(500n);
expect(payload.primarySaleRecipient).toBe(TEST_ACCOUNT_A.address);
expect(payload.uri).toBe("");
expect(payload.pricePerToken).toBe(200000000000000000n);
expect(payload.quantity).toBe(10n);
expect(payload.currency).toBe(erc20TokenContract.address);
expect(payload.validityStartTimestamp).toBe(1635724n);
expect(payload.validityEndTimestamp).toBe(1867260n);
expect(payload.uid).toBe(toHex("abcdef1234567890", { size: 32 }));
expect(signature.length).toBe(132);
});
});
@@ -6,7 +6,7 @@ import {
import type { ThirdwebContract } from "../../../contract/contract.js";
import type { BaseTransactionOptions } from "../../../transaction/types.js";
import { dateToSeconds, tenYearsFromNow } from "../../../utils/date.js";
import type { Hex } from "../../../utils/encoding/hex.js";
import { type Hex, isHex, stringToHex } from "../../../utils/encoding/hex.js";
import { randomBytesHex } from "../../../utils/random.js";
import type { Account } from "../../../wallets/interfaces/wallet.js";
import { name } from "../../common/read/name.js";
@@ -139,8 +139,14 @@ export async function generateMintSignature(
erc20Address: contract.address,
});
})(),
// uid computation
mintRequest.uid || (await randomBytesHex()),
((): Hex => {
if (mintRequest.uid) {
return isHex(mintRequest.uid)
? mintRequest.uid
: stringToHex(mintRequest.uid, { size: 32 });
}
return randomBytesHex();
})(),
// ERC20Permit (EIP-712) spec differs from signature mint 721, 1155.
// it uses the token name in the domain separator
name({
@@ -190,7 +196,7 @@ type GeneratePayloadInput = {
currency?: Address;
validityStartTimestamp?: Date;
validityEndTimestamp?: Date;
uid?: Hex;
uid?: string;
} & ({ quantity: string } | { quantityWei: bigint });
export const MintRequest20 = [
@@ -137,4 +137,35 @@ describe.runIf(process.env.TW_SECRET_KEY)("generateMintSignature20", () => {
);
expect(signature.length).toBe(132);
});
it("should automatically encode a provided string uid", async () => {
const { payload, signature } = await generateMintSignature({
mintRequest: {
to: TEST_ACCOUNT_B.address,
quantity: "0.005",
primarySaleRecipient: TEST_ACCOUNT_A.address,
price: "0.2",
currency: erc20TokenContract.address,
validityStartTimestamp: new Date(1635724800),
validityEndTimestamp: new Date(1867260800),
uid: "abcdef1234567890",
},
account: TEST_ACCOUNT_A,
contract: erc20Contract,
});
expect(payload.to).toBe("0x70997970C51812dc3A010C7d01b50e0d17dc79C8");
expect(payload.primarySaleRecipient).toBe(
"0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266",
);
expect(payload.quantity).toBe(5000000000000000n);
expect(payload.price).toBe(200000000000000000n);
expect(payload.currency).toBe(erc20TokenContract.address);
expect(payload.validityStartTimestamp).toBe(1635724n);
expect(payload.validityEndTimestamp).toBe(1867260n);
expect(payload.uid).toBe(
"0x6162636465663132333435363738393000000000000000000000000000000000",
);
expect(signature.length).toBe(132);
});
});
@@ -1,5 +1,5 @@
import type { AbiParameterToPrimitiveType, Address } from "abitype";
import type { Hex } from "viem";
import { type Hex, isHex, stringToHex } from "viem";
import {
NATIVE_TOKEN_ADDRESS,
isNativeTokenAddress,
@@ -131,7 +131,14 @@ export async function generateMintSignature(
return "";
})(),
// uid computation
mintRequest.uid || (await randomBytesHex()),
((): Hex => {
if (mintRequest.uid) {
return isHex(mintRequest.uid)
? mintRequest.uid
: stringToHex(mintRequest.uid, { size: 32 });
}
return randomBytesHex();
})(),
]);
const startTime = mintRequest.validityStartTimestamp || new Date(0);
@@ -212,7 +219,7 @@ type GeneratePayloadInput = {
currency?: Address;
validityStartTimestamp?: Date;
validityEndTimestamp?: Date;
uid?: Hex;
uid?: string;
};
const MintRequest721 = [
@@ -156,6 +156,43 @@ describe.runIf(process.env.TW_SECRET_KEY)(
expect(signature.length).toBe(132);
});
it("should automatically encode a provided string uid", async () => {
const { payload, signature } = await generateMintSignature({
mintRequest: {
to: TEST_ACCOUNT_B.address,
royaltyRecipient: TEST_ACCOUNT_B.address,
royaltyBps: 500,
primarySaleRecipient: TEST_ACCOUNT_A.address,
metadata: "https://example.com/token",
price: "0.2",
currency: erc20TokenContract.address,
validityStartTimestamp: new Date(1635724800),
validityEndTimestamp: new Date(1867260800),
uid: "abcdef1234567890",
},
account: TEST_ACCOUNT_A,
contract: erc721Contract,
});
expect(payload.to).toBe("0x70997970C51812dc3A010C7d01b50e0d17dc79C8");
expect(payload.royaltyRecipient).toBe(
"0x70997970C51812dc3A010C7d01b50e0d17dc79C8",
);
expect(payload.royaltyBps).toBe(500n);
expect(payload.primarySaleRecipient).toBe(
"0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266",
);
expect(payload.uri).toBe("https://example.com/token");
expect(payload.price).toBe(200000000000000000n);
expect(payload.currency).toBe(erc20TokenContract.address);
expect(payload.validityStartTimestamp).toBe(1635724n);
expect(payload.validityEndTimestamp).toBe(1867260n);
expect(payload.uid).toBe(
"0x6162636465663132333435363738393000000000000000000000000000000000",
);
expect(signature.length).toBe(132);
});
it("should default sale recipient to the contract's primarySaleRecipient when empty", async () => {
const { payload, signature } = await generateMintSignature({
mintRequest: {