
Solana NFT: Chỉnh sửa NFT nén (2023)
Solana NFT: Tại sao cần chỉnh sửa
Là một nhà phát triển trên Solana, việc tương tác với NFT nén có thể khá khó khăn. Tuy nhiên, điều đó không còn là trở ngại nhờ khả năng chỉnh sửa hoặc thay đổi NFT nén thông qua chương trình Bubblegum.
Khi một NFT nén được mint, bằng chứng Merkle phải được cung cấp cùng với chỉ thị giao dịch để thực hiện bất kỳ thay đổi nào đối với NFT đó (ví dụ: chuyển, đốt). Nếu bạn chưa quen với khái niệm này, bằng chứng Merkle là một tập hợp các hàm băm có thể chứng minh rằng lá thuộc về cây.
Trong hướng dẫn này, chúng ta sẽ tìm hiểu:
- Tầm quan trọng của việc chỉnh sửa NFT nén.
- Cách thiết lập thao tác đốt và chuyển NFT nén.
- Tầm quan trọng của API Tiêu chuẩn Tài sản Kỹ thuật số (DAS).
Solana NFT: Điều kiện tiên quyết
Trước khi bắt đầu, hãy đảm bảo bạn có:
- Kiến thức cơ bản về JavaScript/TypeScript.
- Một số hiểu biết về NFT nén.
- Đã cài đặt Git.
- npm hoặc yarn.
- Một bản sao của kho lưu trữ ví dụ, có sẵn tại đây.
Solana NFT: Thiết lập môi trường
- Sao chép kho lưu trữ ví dụ của chúng tôi:
git clone- Chuyển vào thư mục dự án:
cd compression-examples- Cài đặt npm:
npm install- Tạo tệp .env tại thư mục gốc:
API_KEY=YOUR_API_KEY
SECRET_KEY=YOUR_WALLET_SECRET_KEYTrong tệp này, hãy nhập khóa Helius API và khóa bí mật của ví thanh toán. Đảm bảo ví này có Solana để tài trợ cho các giao dịch.
Solana NFT: Các thao tác chỉnh sửa
Để chỉnh sửa, bạn cần lấy các bằng chứng hiện tại cho chỉ thị, cùng với thông tin về quyền sở hữu, thẩm quyền và các hàm băm nén hiện tại của tài sản. Bạn cần truy xuất dữ liệu này từ API Tiêu chuẩn Tài sản Kỹ thuật số (DAS), giúp đơn giản hóa việc tương tác với tài sản nén.
Danh sách thao tác chỉnh sửa hiện có thông qua Bubblegum gồm:
- Mint
- Chuyển
- Đốt
- Ủy quyền, Hủy ủy quyền
- Quy đổi, Hủy quy đổi
- Giải nén
- Xác minh người tạo, Thiết lập và xác minh người tạo
- Xác minh bộ sưu tập, Thiết lập và xác minh bộ sưu tập
Để tương tác với tài sản nén, bạn cần đảm bảo trả về:
- getAssetProof - Truy xuất bằng chứng để tính toán.
- getAsset - Truy xuất hàm băm người tạo, hàm băm dữ liệu, nonce của lá và chủ sở hữu/người được ủy quyền của lá hiện tại.
- getBubblegumAuthorityPDA - Địa chỉ do chương trình dẫn xuất từ ID cây.
Trong các ví dụ này, chúng ta sẽ tìm hiểu cách đốt và chuyển những NFT nén mà chúng ta sẽ mint.
NFT: Chuyển
Thao tác chuyển cho phép bạn chuyển một NFT nén từ ví của chủ sở hữu này sang ví của chủ sở hữu khác. Cả hoạt động bán và niêm yết NFT nén đều sử dụng chỉ thị này để hiển thị giao dịch bán trên các sàn giao dịch.
Để thiết lập thao tác này trong kho lưu trữ hiện tại, chúng ta sẽ mở tệp utils.ts và xem xét hàm transferAsset.
Chúng ta cần truyền vào các thông tin sau:
- Kết nối - Một kết nối Solana RPC.
- Chủ sở hữu hiện tại - Khóa công khai của chủ sở hữu NFT hiện tại.
- Chủ sở hữu mới của NFT - Khóa công khai của chủ sở hữu mới.
- ID tài sản - Cho phép chúng ta lấy thông tin hiện tại của tài sản cụ thể được truyền vào.
Trong hàm transferAsset mẫu bên dưới, ban đầu chúng ta yêu cầu bằng chứng tài sản và thiết lập một đối tượng có tên proofPath. Đây là yêu cầu được gửi đến DAS API để trả về bằng chứng cho tài sản bạn muốn chuyển. Sau đó, bằng chứng này được tính toán để truyền vào chỉ thị giao dịch.
export const transferAsset = async (
connectionWrapper: WrappedConnection,
owner: Keypair,
newOwner: Keypair,
assetId: string
) => {
console.log(
`Transfering asset ${assetId} from ${owner.publicKey.toBase58()} to ${newOwner.publicKey.toBase58()}.
This will depend on indexer api calls to fetch the necessary data.`
);
let assetProof = await connectionWrapper.getAssetProof(assetId);
if (!assetProof?.proof || assetProof.proof.length === 0) {
throw new Error("Proof is empty");
}
let proofPath = assetProof.proof.map((node: string) => ({
pubkey: new PublicKey(node),
isSigner: false,
isWritable: false,
}));
console.log("Successfully got proof path from RPC.");
// Additional code...
};Cần trả về bằng chứng tài sản vì bạn phải cung cấp trạng thái hiện tại của tài sản nén. Đây là yêu cầu bắt buộc đối với mọi lần chuyển NFT nén. Bạn tính toán bằng chứng này trong proofPath để truyền vào chỉ thị giao dịch mà chúng ta sẽ sớm xem xét.
Sau khi có đường dẫn bằng chứng, chúng ta có thể sử dụng getAsset trên DAS API để trả về nonce của lá, người được ủy quyền của lá, hàm băm dữ liệu và hàm băm người tạo.
Chúng ta có thể thiết lập như sau:
export const transferAsset = async (
connectionWrapper: WrappedConnection,
owner: Keypair,
newOwner: Keypair,
assetId: string
) => {
// Previous code //
// Calling getAsset from DAS
const rpcAsset = await connectionWrapper.getAsset(assetId);
console.log(
"Successfully got asset from RPC. Current owner: " +
rpcAsset.ownership.owner
);
if (rpcAsset.ownership.owner !== owner.publicKey.toBase58()) {
throw new Error(
`NFT is not owned by the expected owner. Expected ${owner.publicKey.toBase58()} but got ${
rpcAsset.ownership.owner
}.`
);
}
// Leaf nonce from getAsset call.
const leafNonce = rpcAsset.compression.leaf_id;
// Locating Tree Authority.
const treeAuthority = await getBubblegumAuthorityPDA(
new PublicKey(assetProof.tree_id)
);
// Leaf Owner/Delegate from getAsset call.
const leafDelegate = rpcAsset.ownership.delegate
? new PublicKey(rpcAsset.ownership.delegate)
: new PublicKey(rpcAsset.ownership.owner);
);
};Bạn có thể thấy yêu cầu getAsset đang được thực hiện để trả về chủ sở hữu/người được ủy quyền và nonce của lá. Chúng ta cũng xác định thẩm quyền cây chỉ bằng ID cây (được trả về từ yêu cầu assetProof).
Sau khi đã xác định các giá trị này, chúng ta có thể bắt đầu tạo chỉ thị giao dịch.
export const transferAsset = async (
connectionWrapper: WrappedConnection,
owner: Keypair,
newOwner: Keypair,
assetId: string
) => {
// Previous code //
// Transfer Instruction.
let transferIx = createTransferInstruction(
{
treeAuthority, // Tree authority
leafOwner: new PublicKey(rpcAsset.ownership.owner), // Current NFT owner
leafDelegate: leafDelegate, // Leaf delegate/owner returned
newLeafOwner: newOwner.publicKey, // New wallet to transfer our NFT.
merkleTree: new PublicKey(assetProof.tree_id), // Merkle tree public key.
logWrapper: SPL_NOOP_PROGRAM_ID, // NOOP program ID.
compressionProgram: SPL_ACCOUNT_COMPRESSION_PROGRAM_ID, // Compression Program ID.
anchorRemainingAccounts: proofPath, // Proofs computed to pass in
},
{
root: bufferToArray(bs58.decode(assetProof.root)), // Root from getAssetProof returned
dataHash: bufferToArray(
bs58.decode(rpcAsset.compression.data_hash.trim()) // Data Hash from getAsset
),
creatorHash: bufferToArray(
bs58.decode(rpcAsset.compression.creator_hash.trim()) // Creator Hash from getAsset
),
nonce: leafNonce, // Leaf nonce from getAsset
index: leafNonce,
}
);
};Để thực hiện việc này cho một tài sản được chuyển, bạn cần truyền vào tất cả biến được trả về từ getAsset, getAssetProof và Metaplex SDK: người được ủy quyền/chủ sở hữu của lá, hàm băm dữ liệu, gốc và nonce của lá.
Bạn cũng sẽ thấy chúng ta truyền vào một vài ID chương trình. Đây là các khóa công khai của chương trình nén và noop được nhập ở đầu tệp. Ngoài ra, bạn có thể truyền các giá trị này dưới dạng khóa công khai.
Sau khi hoàn tất, chúng ta có thể thiết lập việc gửi chỉ thị giao dịch:
export const transferAsset = async (
connectionWrapper: WrappedConnection,
owner: Keypair,
newOwner: Keypair,
assetId: string
) => {
// Previous code //
const tx = new Transaction().add(transferIx);
tx.feePayer = owner.publicKey;
try {
const sig = await sendAndConfirmTransaction(
connectionWrapper,
tx,
[owner],
{
commitment: "confirmed",
skipPreflight: true,
}
);
return sig;
} catch (e) {
console.error("Failed to transfer compressed asset", e);
throw e;
}
};Trong đoạn mã trên, chúng ta xác định giao dịch bằng tx và thêm kiểu Transaction cho transferIx từ solana/web3.js .
Sau đó, chúng ta gửi giao dịch bằng cách truyền vào:
- Kết nối Solana
- Các chỉ thị giao dịch
- Bên thanh toán
- Mức cam kết
Bây giờ, chúng ta có thể gửi giao dịch này bằng cách chạy:
npm run e2eHàm này sẽ mint một NFT nén và bộ sưu tập của NFT đó, sau đó chuyển NFT đến ví được chỉ định.
ID tài sản vì chúng ta sẽ sử dụng ID này cho thao tác đốt.
Dưới đây là toàn bộ hàm transferAsset:
export const transferAsset = async (
connectionWrapper: WrappedConnection,
owner: Keypair,
newOwner: Keypair,
assetId: string
) => {
console.log(
`Transfering asset ${assetId} from ${owner.publicKey.toBase58()} to ${newOwner.publicKey.toBase58()}.
This will depend on indexer api calls to fetch the necessary data.`
);
let assetProof = await connectionWrapper.getAssetProof(assetId);
if (!assetProof?.proof || assetProof.proof.length === 0) {
throw new Error("Proof is empty");
}
let proofPath = assetProof.proof.map((node: string) => ({
pubkey: new PublicKey(node),
isSigner: false,
isWritable: false,
}));
console.log("Successfully got proof path from RPC.");
const rpcAsset = await connectionWrapper.getAsset(assetId);
console.log(
"Successfully got asset from RPC. Current owner: " +
rpcAsset.ownership.owner
);
if (rpcAsset.ownership.owner !== owner.publicKey.toBase58()) {
throw new Error(
`NFT is not owned by the expected owner. Expected ${owner.publicKey.toBase58()} but got ${
rpcAsset.ownership.owner
}.`
);
}
const leafNonce = rpcAsset.compression.leaf_id;
const treeAuthority = await getBubblegumAuthorityPDA(
new PublicKey(assetProof.tree_id)
);
const leafDelegate = rpcAsset.ownership.delegate
? new PublicKey(rpcAsset.ownership.delegate)
: new PublicKey(rpcAsset.ownership.owner);
let transferIx = createTransferInstruction(
{
treeAuthority,
leafOwner: new PublicKey(rpcAsset.ownership.owner),
leafDelegate: leafDelegate,
newLeafOwner: newOwner.publicKey,
merkleTree: new PublicKey(assetProof.tree_id),
logWrapper: SPL_NOOP_PROGRAM_ID,
compressionProgram: SPL_ACCOUNT_COMPRESSION_PROGRAM_ID,
anchorRemainingAccounts: proofPath,
},
{
root: bufferToArray(bs58.decode(assetProof.root)),
dataHash: bufferToArray(
bs58.decode(rpcAsset.compression.data_hash.trim())
),
creatorHash: bufferToArray(
bs58.decode(rpcAsset.compression.creator_hash.trim())
),
nonce: leafNonce,
index: leafNonce,
}
);
const tx = new Transaction().add(transferIx);
tx.feePayer = owner.publicKey;
try {
const sig = await sendAndConfirmTransaction(
connectionWrapper,
tx,
[owner],
{
commitment: "confirmed",
skipPreflight: true,
}
);
return sig;
} catch (e) {
console.error("Failed to transfer compressed asset", e);
throw e;
}
};NFT: Đốt
Đốt cNFT sẽ xóa NFT khỏi cây Merkle. Điều này hỗ trợ nhiều trường hợp sử dụng, như đốt để nhận phần thưởng, loại bỏ tài sản rác hoặc đáp ứng các yêu cầu tùy chỉnh. Cách này đã được sử dụng trong đợt mint của Tensor, khi 10 cNFT được đốt để mint 1 cNFT mới.
Để đốt một NFT nén, chúng ta cần truyền vào các thông tin sau:
- Kết nối - Một kết nối Solana RPC.
- Chủ sở hữu hiện tại - Khóa công khai của chủ sở hữu NFT hiện tại.
- ID tài sản - Cho phép chúng ta lấy thông tin hiện tại của tài sản cụ thể được truyền vào.
Bạn có thể thấy điều này trong utils.ts, tại burnAsset:
export const burnAsset = async (
connectionWrapper: WrappedConnection,
owner: Keypair,
assetId?: string
) => {
let assetProof = await connectionWrapper.getAssetProof(assetId);
const rpcAsset = await connectionWrapper.getAsset(assetId);
const leafNonce = rpcAsset.compression.leaf_id;
let proofPath = assetProof.proof.map((node: string) => ({
pubkey: new PublicKey(node),
isSigner: false,
isWritable: false,
}));
const treeAuthority = await getBubblegumAuthorityPDA(
new PublicKey(assetProof.tree_id)
);
const leafDelegate = rpcAsset.ownership.delegate
? new PublicKey(rpcAsset.ownership.delegate)
: new PublicKey(rpcAsset.ownership.owner);
// Remaining code //
}Phần này giống với transferAsset vì sử dụng getAssetProof để trả về gốc và ID cây. Bạn cũng gọi getAsset để trả về hàm băm người tạo, hàm băm dữ liệu, nonce của lá và chủ sở hữu hiện tại.
Nhờ đó, bạn có thể dễ dàng xác định những giá trị cần được trả về tại đây để chỉnh sửa tài sản nén.
Bây giờ, chúng ta có thể thiết lập chỉ thị giao dịch bên dưới đoạn mã này:
export const burnAsset = async (
connectionWrapper: WrappedConnection,
owner: Keypair,
assetId?: string
) => {
// Previous code //
// Burn transaction instruction //
const burnIx = createBurnInstruction(
{
treeAuthority,
leafOwner: new PublicKey(rpcAsset.ownership.owner),
leafDelegate,
merkleTree: new PublicKey(assetProof.tree_id),
logWrapper: SPL_NOOP_PROGRAM_ID,
compressionProgram: SPL_ACCOUNT_COMPRESSION_PROGRAM_ID,
anchorRemainingAccounts: proofPath,
},
{
root: bufferToArray(bs58.decode(assetProof.root)),
dataHash: bufferToArray(
bs58.decode(rpcAsset.compression.data_hash.trim())
),
creatorHash: bufferToArray(
bs58.decode(rpcAsset.compression.creator_hash.trim())
),
nonce: leafNonce,
index: leafNonce,
}
);
// Later code... //
};Sau khi thiết lập thao tác đốt, chúng ta có thể gửi giao dịch theo cách rất giống với chỉ thị chuyển:
export const burnAsset = async (
connectionWrapper: WrappedConnection,
owner: Keypair,
assetId?: string
) => {
// Previous code //
const tx = new Transaction().add(burnIx);
tx.feePayer = owner.publicKey;
try {
const sig = await sendAndConfirmTransaction(
connectionWrapper,
tx,
[owner],
{
commitment: "confirmed",
skipPreflight: true,
}
);
return sig;
} catch (e) {
console.error("Failed to burn compressed asset", e);
throw e;
}
}Sau đó, chúng ta gửi giao dịch bằng cách truyền vào:
- Kết nối Solana
- Các chỉ thị giao dịch
- Bên thanh toán
- Mức cam kết
Sau khi hàm này chạy, tài sản có ID bạn cung cấp sẽ bị đốt. Thao tác này sẽ xóa dữ liệu tài sản khỏi cây và áp dụng cờ burnt=true cho tài sản nén. Theo mặc định, các tài sản đã đốt này vẫn được trả về khi bạn sử dụng DAS API.
Bây giờ, trong terminal, bạn có thể chạy:
npm run burn -- --assetId=Solana NFT: Kết luận
NFT nén mang đến một cách linh hoạt để tương tác với hệ sinh thái NFT trên Solana. Thông qua bằng chứng Merkle lấy từ DAS API và các chỉ thị giao dịch chính xác, nhà phát triển có thể chỉnh sửa và tương tác với những tài sản kỹ thuật số độc đáo này. Khi hiểu rõ các đặc điểm của thao tác chuyển và đốt, nhà phát triển có thể xây dựng những ứng dụng đáng tin cậy hơn.
Bài viết liên quan
Đăng ký nhận tin từ Helius
Luôn cập nhật những thông tin mới nhất về phát triển Solana và nhận thông báo khi chúng tôi đăng bài


