Features
Execute Asset Signing
The MPL Core Execute instruction introduces the concept of Asset Signers to MPL Core Assets.
These Asset Signers act as Signers on behalf of the Asset itself which unlocks the ability for MPL Core Assets
- to transfer out Solana and SPL Tokens.
- to become the authority of other accounts.
- to perform other actions and validations that have been assigned to the
assetSignerPda
that require transaction/instruction/CPI signing.
MPL Core Assets have the ability to sign and submit transactions/CPIs to the blockchain. This effectively gives the Core Asset it's own wallet in the form of an assetSigner
.
Asset Signer PDA
Assets are now able to access the assetSignerPda
account/address which allows the execute
instruction on the MPL Core program to pass through additional instructions sent to it to sign the CPI instructions with the assetSignerPda
.
This allows the assetSignerPda
account to effectively own and execute account instructions on behalf of the current asset owner.
You can think of the assetSignerPda
as a wallet attached to a Core Asset.
findAssetSignerPda()
const assetId = publickey('11111111111111111111111111111111')
const assetSignerPda = findAssetSignerPda(umi, { asset: assetId })
Execute Instruction
Overview
The execute
instruction allows users to pass in the Core Asset and also some pass through instructions that will get signed by the AssetSigner when it hits the MPL Core programs execute
instruction on chain.
An overview of the execute
instruction and it's args.
const executeIx = await execute(umi, {
{
// The asset via `fetchAsset()` that is signing the transaction.
asset: AssetV1,
// The collection via `fetchCollection()`
collection?: CollectionV1,
// Either a TransactionBuilder | Instruction[]
instructions: ExecuteInput,
// Additional Signers that will be required for the transaction/instructions.
signers?: Signer[]
}
})
Validation
assetSignerPda Validation
The MPL Core Execute instruction will validate that the current Asset owner has also signed the transaction. This insures only the current Asset Owner can execute transactions while using the assetSignerPda
with the execute
instruction.
Examples
Transferring SOL From the Asset Signer
In the following example we transfer SOL that had been sent to the assetSignerPda
to a destination of our choice.
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)
// Optional - If Asset is part of collection fetch the collection object
const collection =
asset.updateAuthority.type == 'Collection' && asset.updateAuthority.address
? await fetchCollection(umi, asset.updateAuthority.address)
: undefined
// Asset signer has a balance of 1 SOL in the account.
const assetSignerPda = findAssetSignerPda(umi, { asset: assetId })
// Destination account we wish to transfer the SOL to.
const destination = publickey('2222222222222222222222222222222222')
// A standard `transferSol()` transactionBuilder.
const transferSolIx = transferSol(umi, {
// Create a noopSigner as the assetSigner will sign later during CPI
source: createNoopSigner(publicKey(assetSigner)),
// Destination address
destination,
// Amount you wish to transfer
amount: sol(0.5),
})
// Call the `execute` instruction and send to the chain.
const res = await execute(umi, {
// Execute instruction(s) with this asset
asset,
// If Asset is part of collection pass in collection object via `fetchCollection()`
collection,
// The transactionBuilder/instruction[] to execute
instructions: transferSolIx,
}).sendAndConfirm(umi)
console.log({ res })
Transferring SPL Tokens From the Asset Signer
In the following example we transfer some of our SPL Token balance from the assetSignerPda
account to a destination.
This example is based on the best practices in regards to derived tokens accounts for a base wallet address. If tokens are not in their correctly derived token account based on the assetSignerPda
address then this example will need adjusting.
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)
// Optional - If Asset is part of collection fetch the collection object
const collection =
asset.updateAuthority.type == 'Collection' && asset.updateAuthority.address
? await fetchCollection(umi, asset.updateAuthority.address)
: undefined
const splTokenMint = publickey('2222222222222222222222222222222222')
// Asset signer has a balance of tokens.
const assetSignerPda = findAssetSignerPda(umi, { asset: assetId })
// Destination wallet we wish to transfer the SOL to.
const destinationWallet = publickey('3333333333333333333333333333333')
// A standard `transferTokens()` transactionBuilder.
const transferTokensIx = transferTokens(umi, {
// Source is the `assetSignerPda` derived Token Account
source: findAssociatedTokenPda(umi, {
mint: splTokenMint,
owner: assetSignerPda,
}),
// Destination is the `destinationWallet` derived Token Account
destination: findAssociatedTokenPda(umi, {
mint: splTokenMint,
owner: destinationWallet,
}),
// Amount to send in lamports.
amount: 5000,
})
// Call the `execute` instruction and send to the chain.
const res = await execute(umi, {
// Execute instruction(s) with this asset
asset,
// If Asset is part of collection pass in collection object via `fetchCollection()`
collection,
// The transactionBuilder/instruction[] to execute
instructions: transferTokensIx,
}).sendAndConfirm(umi)
console.log({ res })
Transferring Ownership of an Asset to Another Asset
In the following example we transfer a Core Asset that is owned by another Core Asset, to another.
import {
execute,
fetchAsset,
fetchCollection,
findAssetSignerPda,
transfer,
} from '@metaplex-foundation/mpl-core'
import { publickey } from '@metaplex-foundation/umi'
// Asset we wish to transfer.
const assetId = publickey('11111111111111111111111111111111')
const asset = await fetchAsset(assetId)
// Optional - If Asset is part of collection fetch the collection object
const collection =
asset.updateAuthority.type == 'Collection' && asset.updateAuthority.address
? await fetchCollection(umi, asset.updateAuthority.address)
: undefined
// Asset ID that owns the Asset we wish to transfer.
const sourceAssetId = publickey('2222222222222222222222222222222222')
// The source Asset object.
const sourceAsset = fetchAsset(umi, sourceAssetId)
// Asset signer has a balance of 1 SOL in the account.
const sourceAssetSignerPda = findAssetSignerPda(umi, { asset: assetId })
// Destination account we wish to transfer the SOL to.
const destinationAssetId = publickey('33333333333333333333333333333333')
// Destination Asset signer we wish to transfer the Asset to.
const destinationAssetSignerPda = findAssetSignerPda(umi, {
asset: destinationAssetId,
})
const transferAssetIx = transfer(umi, {
// Asset object via `fetchAsset()`.
asset,
// Optional - Collection object via `fetchCollection()`
collection,
// New Owner of the Asset.
newOwner: destinationAssetSignerPda,
}).sendAndConfirm(umi)
const res = await execute(umi, {
// Execute instruction(s) with this asset
asset,
// If Asset is part of collection pass in collection object via `fetchCollection()`
collection,
// The transactionBuilder/instruction[] to execute
instructions: transferAssetIx,
}).sendAndConfirm(umi)
console.log({ res })