On-chain-ohjelmat siirtävät tokeneita lähettämällä
Cross Program Invocation (CPI) Token Programiin. Ohjelmasi
rakentaa Token Program -ohjeen, toimittaa tarvittavat tilit ja kutsuu
invoke (tai invoke_signed, kun Program Derived Address
allekirjoittaa). Token Program suorittaa ohjeen niillä käyttöoikeuksilla, jotka
ohjelmasi on myöntänyt sille.
Tämä sivu käsittelee kyseisen prosessin token-kohtaisen puolen. CPI-mekanismista
itsestään — miten invoke ja invoke_signed toimivat, miten
allekirjoittajan ja kirjoitettavien tilien käyttöoikeudet välittyvät, sekä
CPI-kustannusmallista — katso
Cross Program Invocation.
Yleiset mallit
Useimmat token CPI:t noudattavat samaa kaavaa: rakenna ohje, välitä token
account -tilit ja auktoriteetti, ja kutsu. Alla oleva esimerkki siirtää
tokeneita TransferChecked-funktiolla, joka tarkistaa mintin ja desimaalit
osana siirtoa.
use anchor_lang::prelude::*;use anchor_spl::token_interface::{transfer_checked, Mint, TokenAccount, TokenInterface, TransferChecked};declare_id!("11111111111111111111111111111111");#[program]pub mod token_cpi {use super::*;pub fn transfer(ctx: Context<TokenTransfer>, amount: u64) -> Result<()> {let cpi_accounts = TransferChecked {from: ctx.accounts.source.to_account_info(),mint: ctx.accounts.mint.to_account_info(),to: ctx.accounts.destination.to_account_info(),authority: ctx.accounts.authority.to_account_info(),};let cpi_ctx = CpiContext::new(ctx.accounts.token_program.to_account_info(),cpi_accounts,);transfer_checked(cpi_ctx, amount, ctx.accounts.mint.decimals)?;Ok(())}}#[derive(Accounts)]pub struct TokenTransfer<'info> {#[account(mut)]pub source: InterfaceAccount<'info, TokenAccount>,pub mint: InterfaceAccount<'info, Mint>,#[account(mut)]pub destination: InterfaceAccount<'info, TokenAccount>,pub authority: Signer<'info>,pub token_program: Interface<'info, TokenInterface>,}
Tokeneiden luominen, polttaminen ja tilien sulkeminen noudattavat samaa kaavaa
eri ohjeella — MintTo, Burn ja CloseAccount Anchorin
anchor_spl::token
-moduulissa tai vastaavilla rakentajilla
pinocchio-token-kirjastossa.
Täydellisiä, ajettavia ohjelmia, jotka käyttävät CPI:tä Token Programiin sekä
Anchorilla että natiivilla Rustilla, löydät
token program -esimerkeistä — erityisesti
"Transfer Tokens" -esimerkistä.
Allekirjoittaminen PDA-auktoriteetilla
Kun token account -tilin auktoriteetti on
Program Derived Address, jonka omistaa ohjelmasi, ohjelma
allekirjoittaa CPI:n itse invoke_signed-funktiolla välittäen PDA:n seedit
allekirjoittajan seedeinä. Anchorissa käytä CpiContext::new_with_signer
seedien kanssa CpiContext::new:n sijaan. Ohje on muuten identtinen yllä
olevien esimerkkien kanssa. Katso
CPIs with PDA Signers täydellisen mekanismin
selitystä varten.
Eräajo
Batch-käsky suorittaa useita Token Program -käskyjä yhden ohjelmakutsun
sisällä. Koska jokaisella CPI:llä on kiinteä laskentakustannus, useampien
tokenoperaatioiden suorittaminen yhdessä eräajo-CPI:ssä kuluttaa vähemmän
laskentayksiköitä kuin erillisen CPI:n
käyttäminen kutakin operaatiota kohti — katso
CPI-kustannusmalli saadaksesi lisätietoa
CPI-kohtaisten kustannusten kertymisestä.
Pienempi laskentakäyttö vähentää prioriteettimaksuja, joita transaktio maksaa laskentayksikköä kohti, ja parantaa sen onnistumismahdollisuuksia — mikä on tärkeintä ohjelmille, jotka suorittavat monia tokenoperaatioita yhdessä käskyssä. Yleisiä käyttötapauksia ovat siirron jakaminen useille vastaanottajille tai monivaiheisen tokenvirran suorittaminen (esimerkiksi synkronointi, siirto ja sulkeminen) yhdessä CPI:ssä.
Batch hyväksyy ainoastaan Token Program -käskyjä alikäskyiksi, eikä erä
voi sisältää toista erää. Tokeneja siirtävien operaatioiden (kuten siirtojen,
luomisten ja polttamisten) osalta ohjelma tarkistaa, että kyseessä olevat tilit
ovat Token Program -omistuksessa ennen niiden suorittamista.
Lähdeviite
Batch-kutsuminen CPI:n kautta
pinocchio-token-paketti tarjoaa
Batch-rakentajan kohteen pinocchio_token::instructions::Batch alla.
Vaiheista jokainen alikäsky erän puskureihin ja lähetä sitten yksi CPI käyttäen
invoke.
Batch:n taustalla on kolme kutsujan toimittamaa puskuria: yksi
serialisoitua instruction data varten, yksi alikäskykohtaisia tiliviittauksia
varten ja yksi CPI:lle välitettäviä tiliäkymiä varten. Luo ne
MaybeUninit-siivuina, välitä ne Batch::new:lle, lisää alikäskyt ja
kutsu sitten:
use core::mem::MaybeUninit;use pinocchio::{account::AccountView, error::ProgramError, ProgramResult};use pinocchio_token::instructions::{Batch, IntoBatch, Transfer};/// Process SwapBatch.////// Performs two transfers in a single batch CPI instead of two separate/// `invoke()` calls.////// Data layout:/// [0..8] amount_a_to_b (u64 LE)/// [8..16] amount_b_to_a (u64 LE)////// Account layout:/// [0] source_a (writable) — token account A/// [1] source_b (writable) — token account B/// [2] authority_a (signer) — authority for account A/// [3] authority_b (signer) — authority for account B/// [4] token_program — SPL Token programpub fn process(accounts: &[AccountView], data: &[u8]) -> ProgramResult {if data.len() < 16 {return Err(ProgramError::InvalidInstructionData);}if accounts.len() < 5 {return Err(ProgramError::NotEnoughAccountKeys);}let amount_a_to_b = u64::from_le_bytes(data[0..8].try_into().unwrap());let amount_b_to_a = u64::from_le_bytes(data[8..16].try_into().unwrap());let source_a = &accounts[0];let source_b = &accounts[1];let authority_a = &accounts[2];let authority_b = &accounts[3];// Two child transfers, each with 3 accounts and 9 bytes of data// (1-byte discriminator + 8-byte amount).const NUM_CHILDREN: usize = 2;const CHILD_ACCOUNTS: usize = 3;const CHILD_DATA_LEN: usize = 9;// Data buffer: 1-byte batch discriminator + per child (2-byte header + data).const DATA_LEN: usize = 1 + NUM_CHILDREN * (2 + CHILD_DATA_LEN);// Account buffers: total accounts across all children.const ACCOUNTS_LEN: usize = NUM_CHILDREN * CHILD_ACCOUNTS;let mut data_buf = [MaybeUninit::uninit(); DATA_LEN];let mut ix_accounts_buf = [MaybeUninit::uninit(); ACCOUNTS_LEN];let mut accounts_buf = [MaybeUninit::uninit(); ACCOUNTS_LEN];let mut batch = Batch::new(&mut data_buf, &mut ix_accounts_buf, &mut accounts_buf)?;Transfer::new(source_a, source_b, authority_a, amount_a_to_b).into_batch(&mut batch)?;Transfer::new(source_b, source_a, authority_b, amount_b_to_a).into_batch(&mut batch)?;batch.invoke()?;Ok(())}
Batch::new kirjoittaa erän erottelijan databufferin ensimmäiseen tavuun ja
palauttaa Batch:n, joka seuraa kuinka paljon kutakin bufferia on käytetty.
Jokainen Token Program -käskynrakentaja (kuten Transfer,
TransferChecked, MintTo ja Burn) toteuttaa
IntoBatch-traitin, joten into_batch lisää kyseisen käskyn datan,
tiliviittaukset ja tilinäkymät erään. Kutsumalla invoke lähetetään koottu
erä yhtenä CPI-kutsuna Token Programiin.
Bufferit rajoittavat erän kapasiteettia. Mitoita databufferi
1 + Σ(2 + child_data_len) tavuun (erotelija sekä 2 tavun otsake ja kunkin
lapsen sarjallistettu data) ja kukin tilibufferi kaikkien lasten tilien
kokonaismäärään. Jos lapsi ei mahdu, into_batch palauttaa
ProgramError::InvalidArgument:n. Viitteenä: Batch::MAX_DATA_LEN on
10 KiB ja Batch::MAX_ACCOUNTS_LEN vastaa ajoympäristön
enimmäis-CPI-tilimäärää.
Eriä varten, joiden koko on tiedossa vasta ajon aikana, ota käyttöön craten
alloc-ominaisuus ja käytä BatchState::new(accounts_len, data_len):a
allokoidaksesi bufferit keolle, sitten kutsu as_batch saadaksesi
Batch:n.
Kun lapsi-käskyn auktoriteetti on ohjelmasi omistama PDA, allekirjoita CPI
käyttämällä invoke_signed:ä invoke:n sijaan ja anna PDA-siemenet
Signer-merkintöinä.
instruction data -asettelu
Erä koodaa lapsensa peräkkäin 255-erottelijan jälkeen. Kaikki tilit välitetään
tasaisessa järjestyksessä ja kunkin lapsen osalta ositetaan num_accounts:lla.
Rakentaja tuottaa täsmälleen tämän siirtoformaatin:
[255] // Batch discriminator// For each child instruction, in order:// [num_accounts: u8] // accounts this instruction consumes// [data_len: u8] // length of this instruction's data// [data: u8; data_len]// the instruction data (begins with its own discriminator)
Erän kapasiteetti on rajoitettu Batch::new:lle välitettyjen bufferien
mukaan: databufferin on pidettävä erotelija sekä jokaisen lapsen otsake ja data,
ja tilibufferien on pidettävä jokaisen lapsen tilit. Mitoita ne suurimman
rakentamasi erän mukaan tai käytä BatchState:ä (alloc-ominaisuuden
kanssa) mitoittaaksesi ne ajon aikana. Yhdistetty CPI on edelleen transaktion
tili- ja kokorajoitusten alainen, joten hyvin suurissa hajautuksissa käytä
Address Lookup Tableseja mahduttaaksesi enemmän tilejä.
Is this page helpful?