기능

Execute 및 Asset 서명

Last updated September 1, 2026

MPL Core Execute 명령은 MPL Core Asset에 Asset Signer 개념을 도입합니다. 이러한 Asset Signer는 Asset 자체를 대신하여 서명자 역할을 수행하며, MPL Core Asset이 다음을 수행할 수 있게 합니다:

  • Solana 및 SPL 토큰을 전송
  • 다른 계정의 authority가 되기
  • 트랜잭션/명령/CPI 서명이 필요한 assetSignerPda에 할당된 기타 작업 및 검증 수행 MPL Core Asset은 블록체인에 트랜잭션/CPI를 서명하고 제출할 수 있습니다. 이는 Core Asset에 assetSigner 형태의 자체 지갑을 효과적으로 제공합니다.

소각 전에 Asset Signer 잔액을 인출하세요

Core Asset을 소각하면 execute 명령이 실패합니다. 프로그램이 Asset을 더 이상 로드할 수 없어 assetSignerPda로 서명할 수 없습니다. 해당 PDA에 남아 있는 SOL, 토큰, 기타 자산은 회수 경로 없이 묶입니다.

Asset Signer PDA

Asset은 이제 assetSignerPda 계정/주소에 액세스할 수 있으며, 이를 통해 MPL Core 프로그램의 execute 명령이 전송된 추가 명령을 통과시켜 assetSignerPda로 CPI 명령에 서명할 수 있습니다. 이를 통해 assetSignerPda 계정은 현재 자산 소유자를 대신하여 계정 명령을 효과적으로 소유하고 실행할 수 있습니다. assetSignerPda를 Core Asset에 연결된 지갑으로 생각할 수 있습니다.

Asset 주소가 아닌 Asset Signer PDA에 자금을 보내세요

assetSignerPda는 Asset 계정과 다른 주소입니다. Asset을 대신하여 SOL과 토큰을 보유할 수 있는 것은 assetSignerPda뿐입니다. Asset 계정 자체를 지갑으로 사용해서는 안 됩니다.

findAssetSignerPda()

const assetId = publickey('11111111111111111111111111111111')
const assetSignerPda = findAssetSignerPda(umi, { asset: assetId })

Execute 명령

개요

execute 명령을 통해 사용자는 Core Asset과 함께 체인상의 MPL Core 프로그램의 execute 명령에 도달할 때 AssetSigner가 서명할 통과 명령을 전달할 수 있습니다. execute 명령과 인수에 대한 개요입니다.

const executeIx = await execute(umi, {
{
// 트랜잭션에 서명하는 `fetchAsset()`을 통한 asset
asset: AssetV1,
// `fetchCollection()`을 통한 collection
collection?: CollectionV1,
// TransactionBuilder | Instruction[] 중 하나
instructions: ExecuteInput,
// 트랜잭션/명령에 필요한 추가 서명자
signers?: Signer[]
}
})

검증

assetSignerPda 검증

MPL Core Execute 명령은 현재 Asset 소유자도 트랜잭션에 서명했는지 검증합니다. 이는 현재 Asset 소유자만 execute 명령으로 assetSignerPda를 사용하여 트랜잭션을 실행할 수 있도록 보장합니다.

Execute 작업 제어

execute 기능은 Freeze Execute 플러그인을 사용하여 제어할 수 있습니다. 이 플러그인을 사용하면 자산의 execute 작업을 동결하여 동결이 해제될 때까지 execute 명령이 처리되지 않도록 할 수 있습니다. Freeze Execute 플러그인은 특히 다음에 유용합니다:

  • 담보 NFT: 필요할 때 기초 자산의 인출 방지
  • 에스크로 없는 프로토콜: 프로토콜 작업 중 execute 기능을 일시적으로 잠금
  • 보안 조치: 복잡한 작업을 실행할 수 있는 자산에 추가 보호 계층 추가 Freeze Execute 플러그인이 활성화되어 frozen: true로 설정되면 플러그인이 frozen: false로 업데이트될 때까지 execute 명령 사용 시도가 차단됩니다.

Asset Signer 잔액이 있는 상태에서 Asset 소각

Core Asset을 소각하면 execute가 영구적으로 비활성화되므로 assetSignerPda에 남은 SOL, 토큰, 중첩 Core Asset을 이동할 수 없습니다.

execute 명령에는 MPL Core 프로그램이 소유한 유효한 Core Asset 계정이 필요합니다. Asset을 소각하면 해당 계정은 더 이상 유효한 Asset이 아닙니다. assetSignerPda 주소 자체는 존재하고 자금을 보유할 수 있지만, 이를 사용할 명령은 더 이상 없습니다.

소각하기 전에 execute로 PDA에서 모두 꺼내세요:

  1. findAssetSignerPda 또는 mplx core asset execute info로 PDA를 파생합니다
  2. PDA가 소유한 SOL, SPL 토큰, Core Asset을 전송합니다
  3. PDA가 비어 있는지 확인합니다
  4. Asset을 소각합니다

예제

Asset Signer에서 SOL 전송

다음 예제에서는 assetSignerPda로 전송된 SOL을 원하는 대상으로 전송합니다.

import {
execute,
findAssetSignerPda,
fetchAsset,
fetchCollection,
} from '@metaplex-foundation/mpl-core'
import { transferSol } from '@metaplex-foundation/mpl-toolbox'
import { publickey, createNoopSigner, sol } from '@metaplex-foundation/umi'
const assetId = publickey('11111111111111111111111111111111')
const asset = await fetchAsset(umi, assetId)
// 선택 사항 - Asset이 컬렉션의 일부인 경우 컬렉션 객체를 가져옴
const collection =
asset.updateAuthority.type == 'Collection' && asset.updateAuthority.address
? await fetchCollection(umi, asset.updateAuthority.address)
: undefined
// Asset signer 계정에 1 SOL 잔액이 있음
const assetSignerPda = findAssetSignerPda(umi, { asset: assetId })
// SOL을 전송하려는 대상 계정
const destination = publickey('2222222222222222222222222222222222')
// 표준 `transferSol()` transactionBuilder
const transferSolIx = transferSol(umi, {
// assetSigner가 나중에 CPI 중에 서명하므로 noopSigner 생성
source: createNoopSigner(publicKey(assetSigner)),
// 대상 주소
destination,
// 전송하려는 금액
amount: sol(0.5),
})
// `execute` 명령을 호출하고 체인에 전송
const res = await execute(umi, {
// 이 asset으로 명령 실행
asset,
// Asset이 컬렉션의 일부인 경우 `fetchCollection()`을 통해 컬렉션 객체 전달
collection,
// 실행할 transactionBuilder/instruction[]
instructions: transferSolIx,
}).sendAndConfirm(umi)
console.log({ res })

Asset Signer에서 SPL 토큰 전송

다음 예제에서는 assetSignerPda 계정의 SPL 토큰 잔액 일부를 대상으로 전송합니다. 이 예제는 기본 지갑 주소에 대한 파생 토큰 계정에 관한 모범 사례를 기반으로 합니다. 토큰이 assetSignerPda 주소를 기반으로 올바르게 파생된 토큰 계정에 없으면 이 예제를 조정해야 합니다.

import {
execute,
findAssetSignerPda,
fetchAsset,
fetchCollection,
} from '@metaplex-foundation/mpl-core'
import {
transferTokens,
findAssociatedTokenPda,
} from '@metaplex-foundation/mpl-toolbox'
import { publickey } from '@metaplex-foundation/umi'
const assetId = publickey('11111111111111111111111111111111')
const asset = await fetchAsset(umi, assetId)
// 선택 사항 - Asset이 컬렉션의 일부인 경우 컬렉션 객체를 가져옴
const collection =
asset.updateAuthority.type == 'Collection' && asset.updateAuthority.address
? await fetchCollection(umi, asset.updateAuthority.address)
: undefined
const splTokenMint = publickey('2222222222222222222222222222222222')
// Asset signer에 토큰 잔액이 있음
const assetSignerPda = findAssetSignerPda(umi, { asset: assetId })
// SOL을 전송하려는 대상 지갑
const destinationWallet = publickey('3333333333333333333333333333333')
// 표준 `transferTokens()` transactionBuilder
const transferTokensIx = transferTokens(umi, {
// Source는 `assetSignerPda` 파생 토큰 계정
source: findAssociatedTokenPda(umi, {
mint: splTokenMint,
owner: assetSignerPda,
}),
// Destination은 `destinationWallet` 파생 토큰 계정
destination: findAssociatedTokenPda(umi, {
mint: splTokenMint,
owner: destinationWallet,
}),
// lamport 단위로 전송할 금액
amount: 5000,
})
// `execute` 명령을 호출하고 체인에 전송
const res = await execute(umi, {
// 이 asset으로 명령 실행
asset,
// Asset이 컬렉션의 일부인 경우 `fetchCollection()`을 통해 컬렉션 객체 전달
collection,
// 실행할 transactionBuilder/instruction[]
instructions: transferTokensIx,
}).sendAndConfirm(umi)
console.log({ res })

Asset 소유권을 다른 Asset으로 전송

다음 예제에서는 다른 Core Asset이 소유한 Core Asset을 또 다른 Asset으로 전송합니다.

import {
execute,
fetchAsset,
fetchCollection,
findAssetSignerPda,
transfer,
} from '@metaplex-foundation/mpl-core'
import { publickey } from '@metaplex-foundation/umi'
// 전송하려는 Asset
const assetId = publickey('11111111111111111111111111111111')
const asset = await fetchAsset(assetId)
// 선택 사항 - Asset이 컬렉션의 일부인 경우 컬렉션 객체를 가져옴
const collection =
asset.updateAuthority.type == 'Collection' && asset.updateAuthority.address
? await fetchCollection(umi, asset.updateAuthority.address)
: undefined
// 전송하려는 Asset을 소유하는 Asset ID
const sourceAssetId = publickey('2222222222222222222222222222222222')
// 소스 Asset 객체
const sourceAsset = fetchAsset(umi, sourceAssetId)
// Asset signer 계정에 1 SOL 잔액이 있음
const sourceAssetSignerPda = findAssetSignerPda(umi, { asset: assetId })
// SOL을 전송하려는 대상 계정
const destinationAssetId = publickey('33333333333333333333333333333333')
// Asset을 전송하려는 대상 Asset signer
const destinationAssetSignerPda = findAssetSignerPda(umi, {
asset: destinationAssetId,
})
const transferAssetIx = transfer(umi, {
// `fetchAsset()`을 통한 Asset 객체
asset,
// 선택 사항 - `fetchCollection()`을 통한 Collection 객체
collection,
// Asset의 새 소유자
newOwner: destinationAssetSignerPda,
}).sendAndConfirm(umi)
const res = await execute(umi, {
// 이 asset으로 명령 실행
asset,
// Asset이 컬렉션의 일부인 경우 `fetchCollection()`을 통해 컬렉션 객체 전달
collection,
// 실행할 transactionBuilder/instruction[]
instructions: transferAssetIx,
}).sendAndConfirm(umi)
console.log({ res })

참고사항

  • 현재 Asset 소유자만 execute를 호출할 수 있습니다 (소유자가 바깥 트랜잭션에 서명해야 함)
  • Freeze Execute 플러그인은 해제될 때까지 execute를 차단할 수 있습니다
  • Asset 소각은 Asset Signer 자금에 대해 되돌릴 수 없습니다. 먼저 PDA를 비우세요
  • assetSignerPda는 Asset 주소에서 결정론적으로 파생되며 Asset이 전송되어도 변하지 않습니다
  • execute 명령에는 명령에 전달되는 payer 계정이 지불하는 프로토콜 수수료가 부과됩니다. payer는 보통 Asset 소유자이지만 assetSignerPda 자체일 수도 있습니다. 이는 Solana 트랜잭션 수수료와는 별개이며, 트랜잭션 수수료는 PDA가 지불할 수 없습니다. 현재 금액은 Protocol Fees 페이지를 참조하세요. 이 수수료는 Asset 계정으로 이체되며 이후 Metaplex 수수료 수집기에 의해 회수됩니다.
  • Asset 계정의 렌트 면제 최소 금액을 초과하는 모든 lamports는 프로토콜 수수료로 취급되어 회수됩니다. SOL과 토큰은 assetSignerPda에 보관하고, Asset 계정에는 절대 보관하지 마세요.
  • assetSignerPda는 시스템 소유 계정입니다. Solana 런타임은 0바이트 계정의 렌트 면제 최소 금액 미만의 0이 아닌 잔액을 남기는 트랜잭션을 거부하므로, 이체는 계정을 비우거나 최소 그 금액을 남겨야 합니다. PDA가 executepayer이기도 하다면 같은 명령에서 프로토콜 수수료도 PDA에서 차감되므로, 남는 잔액을 계산할 때 그 수수료를 포함하세요. 최소 금액은 값을 고정하지 말고 대상 클러스터에서 getMinimumBalanceForRentExemption(0)을 조회하여 확인하세요.

FAQ

Asset을 소각하면 Asset Signer PDA의 자금은 어떻게 되나요?

묶여 회수할 수 없습니다. execute에는 유효한 Core Asset이 필요하므로, 소각 후에도 PDA는 SOL, 토큰, 중첩 Asset을 보유할 수 있지만 이동하도록 서명할 수 있는 것은 없습니다.

소각 후 묶인 Asset Signer 자금을 회수할 수 있나요?

아니요. 원래 Asset 계정 없이 assetSignerPda로 서명할 수 있는 명령은 없습니다.

Asset을 전송하면 Asset Signer 지갑이 묶이나요?

아니요. 전송은 execute를 호출할 수 있는 소유자만 바꿉니다. PDA는 Asset 주소에 연결된 채로 유지됩니다. execute를 비활성화하는 것은 전송이 아니라 소각입니다.