The @instruction decorator

Converting a function to an instruction is as simple as adding the @instruction decorator.

def simple_instruction(signer: Signer):
  # ...

def more_complex_instruction(signer: Signer, data: u64):
  # ...

The parameters of an instruction can include both accounts and regular parameters. On the Rust/Anchor side, accounts are separated and put into an accounts context struct.

Debug logging with print

You can write to Solana's program log with the builtin print function:

def use_print(signer: Signer, account: TokenAccount):
  # Keys are printed in their base58 representation.
  print(signer.key(), account.amount())

  # This works pretty well with f-strings too!
  print(f'Token account {account.key()} has {account.amount()} tokens.')

Structured logging with Events

Seahorse also supports Anchor events, a structured method of logging subscribable information (via Anchor's TypeScript SDK). To use an event, create a class with Event as its parent class:

def MyEvent(Event):
  data: u64

  def __init__(self, data: u64): = u64

When a class inherits from Event, it gains the .emit() function, which is used just like Anchor's emit! to log the event:

event = MyEvent(100)
# Emits the `MyEvent` to program log

Inferred program accounts

In Solana, programs are a special type of accounts. When writing a Solana program, you need to pass in every account that gets used - programs included. Seahorse simplifies this by inferring the necessity of certain program accounts, so that you don't have to include them in your instruction params.

The following table summarizes which programs Seahorse infers

Instructions are special - they are the only place where programs accounts can be inferred. This also means that you can't make certain calls from outside of an instruction (for example, Empty.init(...)), otherwise the compiler won't be able to attach the inferred System program account to anything. For now this just becomes an error, a future update may make this more flexible.

Last updated