Anchor-tapahtumat ovat tyypitettyjä, Borsh-serialisoituja tietueita, jotka sisältyvät ohjelman IDL:ään. Niiden avulla asiakasohjelmat voivat tulkita ohjelman toimintaa ilman ihmisluettavien lokiviestien jäsentämistä.
Tapahtumat sopivat hyvin indeksointiin ja ilmoituksiin, mutta ne eivät ole ohjelman tila. Tallenna kaikki arvot, jotka ohjelman täytyy lukea myöhemmin uudelleen, tilille, ja käsittele tapahtumia onnistuneesta transaktiosta johdettuna tietueena.
Tapahtuman määrittäminen ja lähettäminen
Lisää #[event] structiin ja kutsu emit! sen jälkeen, kun tilan muutos on onnistunut.
use anchor_lang::prelude::*;#[program]pub mod payments {use super::*;pub fn record_payment(ctx: Context<RecordPayment>, amount: u64) -> Result<()> {emit!(PaymentRecorded {authority: ctx.accounts.authority.key(),amount,});Ok(())}}#[derive(Accounts)]pub struct RecordPayment<'info> {pub authority: Signer<'info>,}#[event]pub struct PaymentRecorded {pub authority: Pubkey,pub amount: u64,}
emit! kirjoittaa tapahtuman tavut Solanan sol_log_data-syscallilla. Transaktiolokeissa
tulos näkyy muodossa Program data: <base64>. Koodattu arvo alkaa
tapahtuman erottimella (discriminator), jota seuraavat sen Borsh-serialisoidut kentät.
Pidä tapahtumat tiiviinä. Suuret merkkijonot ja vektorit kuluttavat laskentaa, kasvattavat transaktion metadataa ja kärsivät todennäköisemmin lokien katkaisusta.
Loki- ja CPI-tapahtumien valinta
Anchor tukee kahta lähetystapaa:
| Tapa | Tallennuspaikka | Kompromissi |
|---|---|---|
emit! | Ohjelman datalokit | Yksinkertaisempi ja halvempi, mutta lokit voivat katketa |
emit_cpi! | CPI-kutsun sisäisen käskyn instruction data | Kestävämpi metadata, mutta vaatii lisätilejä ja laskentaa |
Jos haluat käyttää CPI-tapahtumia, ota käyttöön Anchorin event-cpi-ominaisuus:
[dependencies]anchor-lang = { version = "1.1.2", features = ["event-cpi"] }
Merkitse sitten käskyn tilien struct #[event_cpi]-attribuutilla ja lähetä tapahtuma
käsittelijästä, jonka konteksti-muuttujan nimi on ctx:
pub fn record_payment(ctx: Context<RecordPayment>, amount: u64) -> Result<()> {emit_cpi!(PaymentRecorded {authority: ctx.accounts.authority.key(),amount,});Ok(())}#[event_cpi]#[derive(Accounts)]pub struct RecordPayment<'info> {pub authority: Signer<'info>,}
#[event_cpi] lisää tapahtuman authority-tilin ja nykyisen program accountin, joita allekirjoitettu
itse-CPI tarvitsee. CPI-tapahtumia ei voi vastaanottaa Anchorin lokitapahtumatilauksen
kautta. Hae sen sijaan transaktio ja pura vastaava sisäinen käsky.
Käytä lohkoketjun tietoja lähteenä
RPC-palveluntarjoajat voivat katkaista ohjelmalokeja. Jos tapahtuman puuttuminen vaikuttaisi saldoihin, oikeuksiin tai muuhun oikeellisuuden kannalta kriittiseen työnkulkuun, johda tulos ohjelman tileistä tai transaktion instruction datasta. Käytä tapahtumia indeksoinnin tehostamiseen, älä ainoana tilan tietueena.
Tilaaminen TypeScriptillä
Anchorin nykyinen TypeScript-asiakasohjelma on @anchor-lang/core. Ohjelman IDL:stä
luotu Program-instanssi voi purkaa vastaavat lokitapahtumat ja toimittaa
ne RPC-lokitilauksen kautta:
const listenerId = program.addEventListener("PaymentRecorded",(event, slot, signature) => {console.log({ event, slot, signature });},"confirmed");// Remove the websocket subscription when it is no longer needed.await program.removeEventListener(listenerId);
Takaisinkutsu saa puretun tapahtumadatan, slotin ja transaktion allekirjoituksen. Tilaukset kattavat vain ne lokit, jotka toimitetaan yhteyden ollessa aktiivinen. Tallenna käsitellyt allekirjoitukset ja täydennä puuttuvat transaktiot yhteyden katkettua, jos kuluttaja tarvitsee täydellisen historian.
Haettujen transaktiolokien purkaminen
Täydennyksiä ja kertaluonteista tarkastelua varten hae transaktio ja anna jokainen
Program data: -sisältö tapahtumakooderille:
const transaction = await connection.getTransaction(signature, {commitment: "confirmed",maxSupportedTransactionVersion: 0});if (!transaction?.meta || transaction.meta.err) {throw new Error("Transaction was not found or did not succeed");}for (const log of transaction.meta.logMessages ?? []) {const prefix = "Program data: ";if (!log.startsWith(prefix)) continue;const decoded = program.coder.events.decode(log.slice(prefix.length));if (decoded) console.log(decoded.name, decoded.data);}
Yksi transaktio voi kutsua useita ohjelmia. Tuotantokäyttöön tarkoitetussa jäsentimessä seuraa
Program <address> invoke- ja success-lokirajoja tai käytä Anchorin tapahtumajäsennintä,
jotta toisen ohjelman Program data: -merkintää ei pureta omanasi.
Epäonnistuneet transaktiot voivat säilyttää lokeja ja sisäisiä käskyjä ennen virhettä
suoritetusta työstä, joten hylkää aina transaktion metadata, jonka err ei ole null,
ennen kummankin tapahtumamuodon indeksointia. Anchorin lokitilaus tekee tämän tarkistuksen
ennen tapahtuman takaisinkutsun toimittamista.
emit_cpi!-tapahtuman osalta etsi itse-CPI kohdasta meta.innerInstructions,
varmista, että sen ohjelmatunnus on sinun ohjelmasi, ja pura sen instruction data base58-muodosta. Ennen kuin poistat
mitään tavuja, tarkista Anchorin
EVENT_IX_TAG_LE:
import bs58 from "bs58";const EVENT_IX_TAG_LE = Buffer.from("e445a52e51cb9a1d", "hex");const instructionData = Buffer.from(bs58.decode(innerInstruction.data));if (!instructionData.subarray(0, 8).equals(EVENT_IX_TAG_LE)) {// This is an ordinary self-CPI, not an emit_cpi! event.continue;}const eventData = instructionData.subarray(8).toString("base64");const decoded = program.coder.events.decode(eventData);
Kun tarkistat sekä ohjelmatunnuksen että tagin, tavallista itse-CPI:tä ei voi erehtyä pitämään tapahtumana.
Tapahtumaskeemojen versiointi
Oletuksena Anchor johtaa 8-tavuisen tapahtuman erottimen tapahtuman nimestä.
Anchor tukee myös nimenomaista erotinta attribuutilla
#[event(discriminator = ...)]. Erottimien täytyy pysyä yksilöllisinä
ohjelman sisällä ja vakaina jokaiselle asiakasohjelmalle, joka purkaa tapahtuman.
Lähetetyn structin kenttien muuttaminen muuttaa sen Borsh-rakennetta. Rikkovan
skeemamuutoksen yhteydessä määritä uusi tapahtuma, kuten PaymentRecordedV2, sen sijaan että
muuttaisit PaymentRecorded-tapahtumaa hiljaisesti. Jatka vanhan tapahtuman lähettämistä
siirtymäaikana, jos nykyiset indeksoijat ovat edelleen riippuvaisia siitä.
Kehittyvälle datalle suosi kiinteän leveyden kenttiä ja tunnisteita, joiden avulla indeksoija voi hakea monipuolisemmat tilitiedot erikseen. Julkaise päivitetty IDL ennen ohjelmapäivitystä tai sen mukana, jotta kuluttajat voivat purkaa uuden tapahtuman heti.
Tapahtumien testaus
Testaa purettu sopimus, älä base64-merkkijonoa. Rekisteröi kuuntelija ennen käskyn lähettämistä, varmista puretut kentät ja transaktion allekirjoitus ja poista kuuntelija aina lopuksi:
let listenerId = 0;const received = new Promise<{event: { authority: PublicKey; amount: BN };signature: string;}>((resolve) => {listenerId = program.addEventListener("PaymentRecorded",(event, _slot, signature) => resolve({ event, signature }),"confirmed");});try {const signature = await program.methods.recordPayment(new BN(500)).rpc();const result = await received;assert.equal(result.signature, signature);assert.equal(result.event.amount.toString(), "500");} finally {await program.removeEventListener(listenerId);}
Testaa myös, että indeksoija ohittaa epäonnistuneiden transaktioiden metadataan jääneet tapahtumatavut,
ja lisää testiaineisto jokaiselle tuetulle tapahtumaversiolle. Jos käytät
emit_cpi!-makroa, testaa purku kohdasta innerInstructions, koska lokikuuntelijat eivät
saa näitä tapahtumia suoraan.
Lisälukemista
Is this page helpful?