Skip to content

Commit

Permalink
Moved and fixed the turbo guide to be general
Browse files Browse the repository at this point in the history
  • Loading branch information
L0STE committed Nov 5, 2024
1 parent 67de2c6 commit 81b8c14
Show file tree
Hide file tree
Showing 3 changed files with 19 additions and 68 deletions.
4 changes: 4 additions & 0 deletions src/components/products/guides/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,10 @@ export const guides = {
{
title: 'Creating an NFT Collection With Candy Machine',
href: '/candy-machine/guides/create-an-nft-collection-on-solana-with-candy-machine',
},
{
title: 'Create deterministic Metadata with Turbo',
href: '/guides/general/create-deterministic-metadata-with-turbo',
}
]
},
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,21 +27,11 @@ Install the required packages for this guide.
npm i @ardrive/turbo-sdk
```

### Assets & Metadata Folder
### Metadata Folder

In this example, we will show you how to upload both the images and metadata, to do so, you'll need to prepare all the assets before starting.
In this example, we will show you how to upload metadata in a deterministic way. To do so, you'll need to prepare all the assets before starting.

Both images and metadata files should follow an incremental naming convention starting from 0.

```
assets/
├─ 0.png
├─ 1.png
├─ 2.png
├─ ...
```

To generate assets and metadata, you can use [one of these methods](/candy-machine/guides/create-an-nft-collection-on-solana-with-candy-machine#image-and-metadata-generators) and save the metadata in the metadata folder like this:
To generate the metadata, you can use [one of these methods](/candy-machine/guides/create-an-nft-collection-on-solana-with-candy-machine#image-and-metadata-generators) and save the metadata follow an incremental naming convention starting from 0 like this:

```
metadata/
Expand Down Expand Up @@ -75,14 +65,14 @@ const turbo = TurboFactory.authenticated({

**Note**: In this example, we explicitly provide the `gatewayUrl`, `paymentServiceConfig`, and `uploadServiceConfig` because we want to configure the environment to work on devnet. For mainnet usage, you can leave these fields empty, and Turbo will default to the mainnet endpoints.

## Upload Images and Metadata with Turbo
## Upload the Metadata

Turbo simplifies the process of uploading entire folders of images and metadata using the `TurboAuthenticatedClient.uploadFolder()` function. This function supports Manifests by default, returning a Manifest ID via `result.manifestResponse?.id`, which can be used for metadata creation and escrow setup.
Turbo simplifies the process of uploading entire folders of metadata using the `TurboAuthenticatedClient.uploadFolder()` function. This function supports Manifests by default, returning a Manifest ID via `result.manifestResponse?.id`, which can be used for metadata creation and escrow setup.

To simplify the process, this guide provides helper function called `uploadAssetsAndMetadata()` that handles the entire workflow.

```javascript
const metadataUploadResponse = await uploadAssetsAndMetadata(turbo);
const metadataUploadResponse = await uploadMetadata(turbo);
```

**Steps of the `uploadAssetsAndMetadata()` helper**
Expand All @@ -91,16 +81,14 @@ const metadataUploadResponse = await uploadAssetsAndMetadata(turbo);

2. If the wallet lacks sufficient Winc, the function uses `TurboAuthenticatedClient.topUpWithTokens()` to top up the required amount by converting lamports to Winc.

3. Once the wallet has enough Winc, the image folder is uploaded using `TurboAuthenticatedClient.uploadFolder()`, which returns a Manifest ID for the images.

4. The metadata folder is then uploaded using `TurboAuthenticatedClient.uploadFolder()`, returning a Manifest ID that can be used for the escrow process.
3. Once the wallet has enough Winc, upload the metadata folder using `TurboAuthenticatedClient.uploadFolder()`, which returns a Manifest ID for the metadata.

### Calculating Required Lamports

```javascript
const requiredLamportsForAssets = await calculateRequiredLamportsForUpload(
const requiredLamportsForMetadata = await calculateRequiredLamportsForUpload(
turbo,
calculateFolderSize(imageFolderPath)
calculateFolderSize(metadataFolderPath)
);
```

Expand Down Expand Up @@ -152,15 +140,15 @@ async function calculateRequiredLamportsForUpload(turbo: TurboAuthenticatedClien
}
```

### Top Up the Wallet and Upload Images
### Top Up the Wallet and Upload Metadata

To top up the wallet, we use the `TurboAuthenticatedClient.topUpWithTokens()` method, specifying the amount of lamports calculated in the previous step. This amount is converted into Winc (Turbo’s token), which is required for the upload process.

**Note**: The top-up process is conditional. If we already have enough Winc in the wallet, the `calculateRequiredLamportsForUpload()` function will return 0, and no top-up will be necessary.

```javascript
// Top up wallet if required
await turbo.topUpWithTokens({tokenAmount: lamportToTokenAmount(requiredLamportsForAssets)});
await turbo.topUpWithTokens({tokenAmount: lamportToTokenAmount(requiredLamportsForMetadata)});
```

After ensuring the wallet has enough Winc, we can proceed with uploading the image folder. This is done using the `TurboAuthenticatedClient.uploadFolder()` method. The upload will return a manifest ID that allows access to the uploaded files, formatted like this: `https://arweave.net/${manifestID}/${nameOfTheFile.extension}.`
Expand All @@ -170,34 +158,10 @@ After ensuring the wallet has enough Winc, we can proceed with uploading the ima

```javascript
// Upload image folder
const imageUploadResponse = await turbo.uploadFolder({
folderPath: imageFolderPath,
dataItemOpts: { tags: [{ name: 'Content-Type', value: 'image/jpeg' }] },
});
```

### Upload the Metadata Files

The upload of the metadata follows the same preparation process as before and will return the manifestID that we need for the Hybrid Escrow:

```javascript
// Calculate and upload metadata folder
const requiredLamportsForMetadata = await calculateRequiredLamportsForUpload(
turbo,
calculateFolderSize(metadataFolderPath)
);

// Top up wallet if required
await turbo.topUpWithTokens({tokenAmount: lamportToTokenAmount(requiredLamportsForMetadata)});

// Upload metadata folder
const metadataUploadResponse = await turbo.uploadFolder({
folderPath: metadataFolderPath,
dataItemOpts: { tags: [{ name: 'Content-Type', value: 'application/json' }] },
});

console.log('Metadata Manifest ID:', metadataUploadResponse.manifestResponse?.id);
return metadataUploadResponse;
```

## Full code Example
Expand Down Expand Up @@ -237,29 +201,14 @@ const metadataFolderPath = path.join(__dirname, './metadata');
uploadServiceConfig: { url: "https://upload.ardrive.dev" },
});

/// Step 2: Upload Images and Metadata
const metadataUploadResponse = await uploadAssetsAndMetadata(turbo);
/// Step 2: Upload Metadata
const metadataUploadResponse = await uploadMetadata(turbo);
} catch (error) {
console.error("Error during execution:", error);
}
})();

async function uploadAssetsAndMetadata(turbo: TurboAuthenticatedClient): Promise<TurboUploadFolderResponse> {
// Calculate and upload image folder
const requiredLamportsForAssets = await calculateRequiredLamportsForUpload(
turbo,
await calculateFolderSize(imageFolderPath)
);

// Top up wallet if required
await turbo.topUpWithTokens({tokenAmount: lamportToTokenAmount(requiredLamportsForAssets)});

// Upload image folder
const imageUploadResponse = await turbo.uploadFolder({
folderPath: imageFolderPath,
dataItemOpts: { tags: [{ name: 'Content-Type', value: 'image/jpeg' }] },
});

async function uploadMetadata(turbo: TurboAuthenticatedClient): Promise<TurboUploadFolderResponse> {
// Calculate and upload metadata folder
const requiredLamportsForMetadata = await calculateRequiredLamportsForUpload(
turbo,
Expand Down Expand Up @@ -318,8 +267,6 @@ async function calculateRequiredLamportsForUpload(turbo: TurboAuthenticatedClien
/// Return the amount of SOL required in Lamports
return Math.floor(requiredSol * 1_000_000_000)
}


```
{% /totem %}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -76,7 +76,7 @@ Manifest allows multiple transactions to be linked under a single base transacti
- ...
- https://arweave.net/manifestID/9999.json

If you're unfamiliar with creating deterministic URIs, you can follow [this guide](create-deterministic-metadata-with-turbo) for a detailed walkthrough. Additionally, you can find instructions on creating a [collection](/core/guides/javascript/how-to-create-a-core-collection-with-javascript) and the [assets](/core/guides/javascript/how-to-create-a-core-nft-asset-with-javascript) required for the Hybrid program to function.
If you're unfamiliar with creating deterministic URIs, you can follow [this guide](/guides/general/create-deterministic-metadata-with-turbo) for a detailed walkthrough. Additionally, you can find instructions on creating a [collection](/core/guides/javascript/how-to-create-a-core-collection-with-javascript) and the [assets](/core/guides/javascript/how-to-create-a-core-nft-asset-with-javascript) required for the Hybrid program to function.

**Note**: Currently, the MPL-Hybrid program randomly picks a number between the min and max URI index provided and does not check to see if the URI is already used. As such, swapping suffers from the [Birthday Paradox](https://betterexplained.com/articles/understanding-the-birthday-paradox/). In order for projects to benefit from sufficient swap randomization, we recommend preparing and uploading a minimum of 250k asset metadata that can be randomly picked from. The more available potential assets the better!

Expand Down

0 comments on commit 81b8c14

Please sign in to comment.