Skip to content

Commit 5a7c24f

Browse files
updateMetadata for edition drop contracts
1 parent 7bbf3e2 commit 5a7c24f

File tree

5 files changed

+194
-8
lines changed

5 files changed

+194
-8
lines changed

.changeset/four-adults-confess.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
"@thirdweb-dev/sdk": patch
3+
---
4+
5+
Expose contract.erc1155.updateMetadata()

packages/sdk/src/evm/core/classes/erc-1155.ts

Lines changed: 38 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,12 @@ import {
1414
type BytesLike,
1515
} from "ethers";
1616
import { QueryAllParams } from "../../../core/schema/QueryParams";
17-
import { NFT, NFTMetadata, NFTMetadataOrUri } from "../../../core/schema/nft";
17+
import {
18+
NFT,
19+
NFTMetadata,
20+
NFTMetadataInput,
21+
NFTMetadataOrUri,
22+
} from "../../../core/schema/nft";
1823
import { resolveAddress } from "../../common/ens/resolveAddress";
1924
import {
2025
ExtensionNotImplementedError,
@@ -959,6 +964,38 @@ export class Erc1155<
959964
},
960965
);
961966

967+
////// ERC1155 Update Metadata Extension //////
968+
969+
/**
970+
* Update the metadata of an NFT
971+
*
972+
* @remarks Update the metadata of an NFT in the connected wallet
973+
*
974+
* @example
975+
* ```javascript
976+
* // The token ID of the NFT you want to update
977+
* const tokenId = 0;
978+
* // The updated metadata of the NFT
979+
* const metadata = {
980+
* name: "Updated NFT",
981+
* description: "This is an updated NFT",
982+
* image: fs.readFileSync("path/to/image.png"), // This can be an image url or file
983+
* }
984+
*
985+
* const result = await contract.erc1155.updateMetadata(tokenId, metadata);
986+
* ```
987+
* @twfeature ERC1155UpdateMetadata
988+
*/
989+
updateMetadata = /* @__PURE__ */ buildTransactionFunction(
990+
async (tokenId: BigNumberish, metadata: NFTMetadataInput) => {
991+
// TODO handle updating regular TokenERC1155 metadata
992+
return assertEnabled(
993+
this.lazyMintable,
994+
FEATURE_EDITION_LAZY_MINTABLE_V2,
995+
).updateMetadata.prepare(tokenId, metadata);
996+
},
997+
);
998+
962999
////// ERC1155 Claimable Extension //////
9631000

9641001
/**

packages/sdk/src/evm/core/classes/internal/erc1155/erc-1155-lazy-mintable.ts

Lines changed: 75 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,12 @@
1-
import type { DropERC1155_V2 } from "@thirdweb-dev/contracts-js";
1+
import type { DropERC1155, DropERC1155_V2 } from "@thirdweb-dev/contracts-js";
22
import { TokensLazyMintedEvent } from "@thirdweb-dev/contracts-js/dist/declarations/src/LazyMint";
33
import { ThirdwebStorage } from "@thirdweb-dev/storage";
4-
import { utils, type providers } from "ethers";
5-
import { NFTMetadata, NFTMetadataOrUri } from "../../../../../core/schema/nft";
4+
import { utils, type providers, BigNumber, BigNumberish } from "ethers";
5+
import {
6+
NFTMetadata,
7+
NFTMetadataInput,
8+
NFTMetadataOrUri,
9+
} from "../../../../../core/schema/nft";
610
import { detectContractFeature } from "../../../../common/feature-detection/detectContractFeature";
711
import { getPrebuiltInfo } from "../../../../common/legacy";
812
import { uploadOrExtractURIs } from "../../../../common/nft";
@@ -176,6 +180,74 @@ export class Erc1155LazyMintable implements DetectableFeature {
176180
},
177181
);
178182

183+
updateMetadata = /* @__PURE__ */ buildTransactionFunction(
184+
async (
185+
tokenId: BigNumberish,
186+
metadata: NFTMetadataInput,
187+
options?: {
188+
onProgress: (event: UploadProgressEvent) => void;
189+
},
190+
) => {
191+
const batchCount = await this.contractWrapper.read("getBaseURICount", []);
192+
if (batchCount.eq(0)) {
193+
throw new Error(
194+
"No base URI set. Please set a base URI before updating metadata",
195+
);
196+
}
197+
const targetTokenId = BigNumber.from(tokenId);
198+
let startTokenId = BigNumber.from(0);
199+
let endTokenId = BigNumber.from(0);
200+
let batchIndex = 0;
201+
for (let i = 0; i < batchCount.toNumber(); i++) {
202+
batchIndex = i;
203+
endTokenId = await this.contractWrapper.read("getBatchIdAtIndex", [
204+
batchIndex,
205+
]);
206+
if (endTokenId.gt(targetTokenId)) {
207+
break;
208+
}
209+
startTokenId = endTokenId;
210+
}
211+
// for the entire batch,
212+
// 1. download all of the metadata as a list of nft metadata
213+
const range = Array.from(
214+
{ length: endTokenId.sub(startTokenId).toNumber() },
215+
(v, k) => k + startTokenId.toNumber(),
216+
);
217+
const metadatas = await Promise.all(
218+
range.map((id) => this.erc1155.getTokenMetadata(id)),
219+
);
220+
// 2. replace the metadata of the tokenId desired
221+
const newMetadatas = [];
222+
for (let i = 0; i < metadatas.length; i++) {
223+
const { id, uri, ...rest } = metadatas[i];
224+
if (BigNumber.from(targetTokenId).eq(BigNumber.from(id))) {
225+
newMetadatas.push(metadata);
226+
} else {
227+
void uri; // linter
228+
newMetadatas.push(rest);
229+
}
230+
}
231+
// 3. re-upload the entire batch with the correct starting number
232+
const batch = await uploadOrExtractURIs(
233+
newMetadatas,
234+
this.storage,
235+
startTokenId.toNumber(),
236+
options,
237+
);
238+
const baseUri = batch[0].substring(0, batch[0].lastIndexOf("/"));
239+
// 4. update the base uri for the entire batch
240+
return Transaction.fromContractWrapper({
241+
contractWrapper: this.contractWrapper as ContractWrapper<DropERC1155>, // TODO contract detection
242+
method: "updateBatchBaseURI",
243+
args: [
244+
batchIndex,
245+
`${baseUri.endsWith("/") ? baseUri : `${baseUri}/`}`,
246+
],
247+
});
248+
},
249+
);
250+
179251
/** ******************************
180252
* PRIVATE FUNCTIONS
181253
*******************************/

packages/sdk/test/evm/edition-drop-v4.test.ts

Lines changed: 76 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -46,9 +46,11 @@ describe("Edition Drop Contract (V4)", async () => {
4646
platform_fee_basis_points: 10,
4747
platform_fee_recipient: adminWallet.address,
4848
},
49-
await sdk.deployer.getLatestBuiltInContractVersion(
50-
EditionDropInitializer.contractType,
51-
),
49+
(
50+
await sdk.deployer.getLatestBuiltInContractVersion(
51+
EditionDropInitializer.contractType,
52+
)
53+
).toString(),
5254
);
5355
bdContract = await sdk.getEditionDrop(address);
5456
});
@@ -252,6 +254,77 @@ describe("Edition Drop Contract (V4)", async () => {
252254
}
253255
});
254256

257+
it("should update metadata", async () => {
258+
const tokens = [
259+
{
260+
name: "test 0",
261+
},
262+
{
263+
name: "test 1",
264+
},
265+
{
266+
name: "test 2",
267+
},
268+
{
269+
name: "test 3",
270+
},
271+
{
272+
name: "test 4",
273+
},
274+
];
275+
const result = await bdContract.createBatch(tokens);
276+
assert.lengthOf(result, tokens.length);
277+
for (const token of tokens) {
278+
const found = result.find(
279+
async (t) => (await t.data()).name === token.name,
280+
);
281+
assert.isDefined(found);
282+
}
283+
284+
const token = await bdContract.get(1);
285+
expect(token.metadata.name).to.eq("test 1");
286+
await bdContract.erc1155.updateMetadata(1, {
287+
name: "test 123",
288+
});
289+
const token2 = await bdContract.get(1);
290+
expect(token2.metadata.name).to.eq("test 123");
291+
});
292+
293+
it("should update metadata from diff batch", async () => {
294+
const tokens = [
295+
{
296+
name: "test 0",
297+
},
298+
{
299+
name: "test 1",
300+
},
301+
{
302+
name: "test 2",
303+
},
304+
];
305+
const result = await bdContract.createBatch(tokens);
306+
assert.lengthOf(result, tokens.length);
307+
308+
const tokens2 = [
309+
{
310+
name: "test 3",
311+
},
312+
{
313+
name: "test 4",
314+
},
315+
];
316+
const result2 = await bdContract.createBatch(tokens2);
317+
assert.lengthOf(result2, tokens2.length);
318+
319+
let token = await bdContract.get("3");
320+
expect(token.metadata.name).to.eq("test 3");
321+
await bdContract.erc1155.updateMetadata(3, {
322+
name: "test 123",
323+
});
324+
token = await bdContract.get("3");
325+
expect(token.metadata.name).to.eq("test 123");
326+
});
327+
255328
it("should allow setting max claims per wallet", async () => {
256329
await bdContract.createBatch([
257330
{ name: "name", description: "description" },

pnpm-lock.yaml

Lines changed: 0 additions & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)