tokencrawler/.venv/lib/python3.9/site-packages/solana/system_program.py
2022-03-17 22:16:30 +01:00

666 lines
21 KiB
Python

"""Library to interface with the system program."""
from __future__ import annotations
from typing import Any, NamedTuple, Union
from solana import sysvar
from solana._layouts.system_instructions import SYSTEM_INSTRUCTIONS_LAYOUT, InstructionType
from solana.publickey import PublicKey
from solana.transaction import AccountMeta, Transaction, TransactionInstruction
from solana.utils.validate import validate_instruction_keys, validate_instruction_type
SYS_PROGRAM_ID: PublicKey = PublicKey("11111111111111111111111111111111")
"""Public key that identifies the System program."""
# Instruction Params
class CreateAccountParams(NamedTuple):
"""Create account system transaction params."""
from_pubkey: PublicKey
""""""
new_account_pubkey: PublicKey
""""""
lamports: int
""""""
space: int
""""""
program_id: PublicKey
""""""
class TransferParams(NamedTuple):
"""Transfer system transaction params."""
from_pubkey: PublicKey
""""""
to_pubkey: PublicKey
""""""
lamports: int
""""""
class AssignParams(NamedTuple):
"""Assign system transaction params."""
account_pubkey: PublicKey
""""""
program_id: PublicKey
""""""
class CreateAccountWithSeedParams(NamedTuple):
"""Create account with seed system transaction params."""
from_pubkey: PublicKey
""""""
new_account_pubkey: PublicKey
""""""
base_pubkey: PublicKey
""""""
seed: str
""""""
lamports: int
""""""
space: int
""""""
program_id: PublicKey
""""""
class CreateNonceAccountParams(NamedTuple):
"""Create nonce account system transaction params."""
from_pubkey: PublicKey
""""""
nonce_pubkey: PublicKey
""""""
authorized_pubkey: PublicKey
""""""
lamports: int
""""""
class CreateNonceAccountWithSeedParams(NamedTuple):
"""Create nonce account with seed system transaction params."""
from_pubkey: PublicKey
""""""
nonce_pubkey: PublicKey
""""""
authorized_pubkey: PublicKey
""""""
lamports: int
""""""
base_pubkey: PublicKey
""""""
seed: str
""""""
class InitializeNonceParams(NamedTuple):
"""Initialize nonce account system instruction params."""
nonce_pubkey: PublicKey
""""""
authorized_pubkey: PublicKey
""""""
class AdvanceNonceParams(NamedTuple):
"""Advance nonce account system instruction params."""
nonce_pubkey: PublicKey
""""""
authorized_pubkey: PublicKey
""""""
class WithdrawNonceParams(NamedTuple):
"""Withdraw nonce account system transaction params."""
nonce_pubkey: PublicKey
""""""
authorized_pubkey: PublicKey
""""""
to_pubkey: PublicKey
""""""
lamports: int
""""""
class AuthorizeNonceParams(NamedTuple):
"""Authorize nonce account system transaction params."""
nonce_pubkey: PublicKey
""""""
authorized_pubkey: PublicKey
""""""
new_authorized_pubkey: PublicKey
""""""
class AllocateParams(NamedTuple):
"""Allocate account with seed system transaction params."""
account_pubkey: PublicKey
""""""
space: int
""""""
class AllocateWithSeedParams(NamedTuple):
"""Allocate account with seed system transaction params."""
account_pubkey: PublicKey
""""""
base_pubkey: PublicKey
""""""
seed: str
""""""
space: int
""""""
program_id: PublicKey
""""""
class AssignWithSeedParams(NamedTuple):
"""Assign account with seed system transaction params."""
account_pubkey: PublicKey
""""""
base_pubkey: PublicKey
""""""
seed: str
""""""
program_id: PublicKey
""""""
def __check_program_id(program_id: PublicKey) -> None:
if program_id != SYS_PROGRAM_ID:
raise ValueError("invalid instruction: programId is not SystemProgram")
def __parse_and_validate_instruction(
instruction: TransactionInstruction,
expected_keys: int,
expected_type: InstructionType,
) -> Any: # Returns a Construct container.
validate_instruction_keys(instruction, expected_keys)
data = SYSTEM_INSTRUCTIONS_LAYOUT.parse(instruction.data)
validate_instruction_type(data, expected_type)
return data
def decode_create_account(instruction: TransactionInstruction) -> CreateAccountParams:
"""Decode a create account system instruction and retrieve the instruction params.
Args:
instruction: The instruction to decode.
Example:
>>> from solana.publickey import PublicKey
>>> from_account, new_account, program_id = PublicKey(1), PublicKey(2), PublicKey(3)
>>> instruction = create_account(
... CreateAccountParams(
... from_pubkey=from_account, new_account_pubkey=new_account,
... lamports=1, space=1, program_id=program_id)
... )
>>> decode_create_account(instruction)
CreateAccountParams(from_pubkey=11111111111111111111111111111112, new_account_pubkey=11111111111111111111111111111113, lamports=1, space=1, program_id=11111111111111111111111111111114)
Returns:
The decoded instruction params.
""" # noqa: E501 # pylint: disable=line-too-long
parsed_data = __parse_and_validate_instruction(instruction, 2, InstructionType.CREATE_ACCOUNT)
return CreateAccountParams(
from_pubkey=instruction.keys[0].pubkey,
new_account_pubkey=instruction.keys[1].pubkey,
lamports=parsed_data.args.lamports,
space=parsed_data.args.space,
program_id=PublicKey(parsed_data.args.program_id),
)
def decode_transfer(instruction: TransactionInstruction) -> TransferParams:
"""Decode a transfer system instruction and retrieve the instruction params.
Args:
instruction: The instruction to decode.
Example:
>>> from solana.publickey import PublicKey
>>> sender, receiver = PublicKey(1), PublicKey(2)
>>> instruction = transfer(
... TransferParams(from_pubkey=sender, to_pubkey=receiver, lamports=1000)
... )
>>> decode_transfer(instruction)
TransferParams(from_pubkey=11111111111111111111111111111112, to_pubkey=11111111111111111111111111111113, lamports=1000)
Returns:
The decoded instruction params.
""" # pylint: disable=line-too-long # noqa: E501
parsed_data = __parse_and_validate_instruction(instruction, 2, InstructionType.TRANSFER)
return TransferParams(
from_pubkey=instruction.keys[0].pubkey, to_pubkey=instruction.keys[1].pubkey, lamports=parsed_data.args.lamports
)
def decode_allocate(instruction: TransactionInstruction) -> AllocateParams:
"""Decode an allocate system instruction and retrieve the instruction params.
Args:
instruction: The instruction to decode.
Example:
>>> from solana.publickey import PublicKey
>>> allocator = PublicKey(1)
>>> instruction = allocate(
... AllocateParams(account_pubkey=allocator,space=65537)
... )
>>> decode_allocate(instruction)
AllocateParams(account_pubkey=11111111111111111111111111111112, space=65537)
Returns:
The decoded instruction params.
""" # pylint: disable=line-too-long # noqa: E501
parsed_data = __parse_and_validate_instruction(instruction, 1, InstructionType.ALLOCATE)
return AllocateParams(account_pubkey=instruction.keys[0].pubkey, space=parsed_data.args.space)
def decode_allocate_with_seed(instruction: TransactionInstruction) -> AllocateWithSeedParams:
"""Decode an allocate with seed system instruction and retrieve the instruction params.
Args:
instruction: The instruction to decode.
Example:
>>> from solana.publickey import PublicKey
>>> allocator, base, program_id = PublicKey(1), PublicKey(2), PublicKey(3)
>>> instruction = allocate(
... AllocateWithSeedParams(
... account_pubkey=allocator,
... base_pubkey=base,
... seed={"length": 4, "chars": "gqln"},
... space=65537,
... program_id=program_id
... )
... )
>>> decode_allocate_with_seed(instruction)
AllocateWithSeedParams(account_pubkey=11111111111111111111111111111112, base_pubkey=11111111111111111111111111111113, seed=Container(length=4, chars=u'gqln'), space=65537, program_id=11111111111111111111111111111114)
Returns:
The decoded instruction params.
""" # pylint: disable=line-too-long # noqa: E501
parsed_data = __parse_and_validate_instruction(instruction, 1, InstructionType.ALLOCATE_WITH_SEED)
return AllocateWithSeedParams(
account_pubkey=instruction.keys[0].pubkey,
base_pubkey=PublicKey(parsed_data.args.base),
seed=parsed_data.args.seed,
space=parsed_data.args.space,
program_id=PublicKey(parsed_data.args.program_id),
)
def decode_assign(instruction: TransactionInstruction) -> AssignParams:
"""Decode an assign system instruction and retrieve the instruction params.
Args:
instruction: The instruction to decode.
Example:
>>> from solana.publickey import PublicKey
>>> account, program_id = PublicKey(1), PublicKey(2)
>>> instruction = assign(
... AssignParams(account_pubkey=account, program_id=program_id)
... )
>>> decode_assign(instruction)
AssignParams(account_pubkey=11111111111111111111111111111112, program_id=11111111111111111111111111111113)
Returns:
The decoded instruction params.
"""
parsed_data = __parse_and_validate_instruction(instruction, 1, InstructionType.ASSIGN)
return AssignParams(account_pubkey=instruction.keys[0].pubkey, program_id=PublicKey(parsed_data.args.program_id))
def decode_assign_with_seed(instruction: TransactionInstruction) -> AssignWithSeedParams:
"""Decode an assign system with seed instruction and retrieve the instruction params."""
raise NotImplementedError("decode_assign_with_seed not implemented")
def decode_create_account_with_seed(instruction: TransactionInstruction) -> CreateAccountWithSeedParams:
"""Decode a create account with seed system instruction and retrieve the instruction params.
Args:
instruction: The instruction to decode.
Returns:
The decoded instruction params.
"""
parsed_data = __parse_and_validate_instruction(instruction, 1, InstructionType.CREATE_ACCOUNT_WITH_SEED)
return CreateAccountWithSeedParams(
from_pubkey=instruction.keys[0].pubkey,
new_account_pubkey=instruction.keys[1].pubkey,
base_pubkey=PublicKey(parsed_data.args.base),
seed=parsed_data.args.seed,
lamports=parsed_data.args.lamports,
space=parsed_data.args.space,
program_id=PublicKey(parsed_data.args.program_id),
)
def decode_nonce_initialize(instruction: TransactionInstruction) -> InitializeNonceParams:
"""Decode a nonce initialize system instruction and retrieve the instruction params."""
raise NotImplementedError("decode_nonce_initialize not implemented")
def decode_nonce_advance(instruction: TransactionInstruction) -> AdvanceNonceParams:
"""Decode a nonce advance system instruction and retrieve the instruction params."""
raise NotImplementedError("decode_nonce_advance not implemented")
def decode_nonce_withdraw(instruction: TransactionInstruction) -> WithdrawNonceParams:
"""Decode a nonce withdraw system instruction and retrieve the instruction params."""
raise NotImplementedError("decode_nonce_withdraw not implemented")
def decode_nonce_authorize(instruction: TransactionInstruction) -> AuthorizeNonceParams:
"""Decode a nonce authorize system instruction and retrieve the instruction params."""
raise NotImplementedError("decode_nonce_authorize not implemented")
def create_account(params: CreateAccountParams) -> TransactionInstruction:
"""Generate an instruction that creates a new account.
Args:
params: The create account params.
Example:
>>> from solana.publickey import PublicKey
>>> from_account, new_account, program_id = PublicKey(1), PublicKey(2), PublicKey(3)
>>> instruction = create_account(
... CreateAccountParams(
... from_pubkey=from_account, new_account_pubkey=new_account,
... lamports=1, space=1, program_id=program_id)
... )
>>> type(instruction)
<class 'solana.transaction.TransactionInstruction'>
Returns:
The instruction to create the account.
"""
data = SYSTEM_INSTRUCTIONS_LAYOUT.build(
dict(
instruction_type=InstructionType.CREATE_ACCOUNT,
args=dict(lamports=params.lamports, space=params.space, program_id=bytes(params.program_id)),
)
)
return TransactionInstruction(
keys=[
AccountMeta(pubkey=params.from_pubkey, is_signer=True, is_writable=True),
AccountMeta(pubkey=params.new_account_pubkey, is_signer=True, is_writable=True),
],
program_id=SYS_PROGRAM_ID,
data=data,
)
def assign(params: Union[AssignParams, AssignWithSeedParams]) -> TransactionInstruction:
"""Generate an instruction that assigns an account to a program.
Args:
params: The assign params.
Example:
>>> from solana.publickey import PublicKey
>>> account, program_id = PublicKey(1), PublicKey(2)
>>> instruction = assign(
... AssignParams(account_pubkey=account, program_id=program_id)
... )
>>> type(instruction)
<class 'solana.transaction.TransactionInstruction'>
"""
if isinstance(params, AssignWithSeedParams):
raise NotImplementedError("assign with key is not implemented")
data = SYSTEM_INSTRUCTIONS_LAYOUT.build(
dict(instruction_type=InstructionType.ASSIGN, args=dict(program_id=bytes(params.program_id)))
)
return TransactionInstruction(
keys=[
AccountMeta(pubkey=params.account_pubkey, is_signer=True, is_writable=True),
],
program_id=SYS_PROGRAM_ID,
data=data,
)
def transfer(params: TransferParams) -> TransactionInstruction:
"""Generate an instruction that transfers lamports from one account to another.
Args:
params: The transfer params.
Example:
>>> from solana.publickey import PublicKey
>>> sender, receiver = PublicKey(1), PublicKey(2)
>>> instruction = transfer(
... TransferParams(from_pubkey=sender, to_pubkey=receiver, lamports=1000)
... )
>>> type(instruction)
<class 'solana.transaction.TransactionInstruction'>
Returns:
The transfer instruction.
"""
data = SYSTEM_INSTRUCTIONS_LAYOUT.build(
dict(instruction_type=InstructionType.TRANSFER, args=dict(lamports=params.lamports))
)
return TransactionInstruction(
keys=[
AccountMeta(pubkey=params.from_pubkey, is_signer=True, is_writable=True),
AccountMeta(pubkey=params.to_pubkey, is_signer=False, is_writable=True),
],
program_id=SYS_PROGRAM_ID,
data=data,
)
def create_account_with_seed(
params: CreateAccountWithSeedParams,
) -> TransactionInstruction:
"""Generate a instruction that creates a new account at an address generated with `from`, a seed, and programId.
Args:
params: account creation params.
Returns:
The instruction to create the account.
"""
data = SYSTEM_INSTRUCTIONS_LAYOUT.build(
dict(
instruction_type=InstructionType.CREATE_ACCOUNT_WITH_SEED,
args=dict(
base=bytes(params.base_pubkey),
seed=params.seed,
lamports=params.lamports,
space=params.space,
program_id=bytes(params.program_id),
),
)
)
keys = [
AccountMeta(pubkey=params.from_pubkey, is_signer=True, is_writable=True),
AccountMeta(pubkey=params.new_account_pubkey, is_signer=False, is_writable=True),
]
if params.base_pubkey != params.from_pubkey:
keys.append(AccountMeta(pubkey=params.base_pubkey, is_signer=True, is_writable=False))
return TransactionInstruction(keys=keys, program_id=SYS_PROGRAM_ID, data=data)
def create_nonce_account(params: Union[CreateNonceAccountParams, CreateNonceAccountWithSeedParams]) -> Transaction:
"""Generate a Transaction that creates a new Nonce account.
Args:
params: The create nonce params.
Returns:
The transaction to create the new nonce account.
"""
if isinstance(params, CreateNonceAccountParams):
create_account_instruction = create_account(
CreateAccountParams(
from_pubkey=params.from_pubkey,
new_account_pubkey=params.nonce_pubkey,
lamports=params.lamports,
space=80, # derived from rust implementation
program_id=SYS_PROGRAM_ID,
)
)
else:
create_account_instruction = create_account_with_seed(
CreateAccountWithSeedParams(
from_pubkey=params.from_pubkey,
new_account_pubkey=params.nonce_pubkey,
base_pubkey=params.base_pubkey,
seed=params.seed,
lamports=params.lamports,
space=80, # derived from rust implementation
program_id=SYS_PROGRAM_ID,
)
)
initialize_nonce_instruction = nonce_initialization(
InitializeNonceParams(
nonce_pubkey=params.nonce_pubkey,
authorized_pubkey=params.authorized_pubkey,
)
)
return Transaction(fee_payer=params.from_pubkey).add(create_account_instruction, initialize_nonce_instruction)
def nonce_initialization(params: InitializeNonceParams) -> TransactionInstruction:
"""Generate an instruction to initialize a Nonce account.
Args:
params: The nonce initialization params.
Returns:
The instruction to initialize the nonce account.
"""
data = SYSTEM_INSTRUCTIONS_LAYOUT.build(
dict(
instruction_type=InstructionType.INITIALIZE_NONCE_ACCOUNT,
args=dict(
authorized=bytes(params.authorized_pubkey),
),
)
)
return TransactionInstruction(
keys=[
AccountMeta(pubkey=params.nonce_pubkey, is_signer=True, is_writable=True),
AccountMeta(pubkey=sysvar.SYSVAR_RECENT_BLOCKHASHES_PUBKEY, is_signer=False, is_writable=False),
AccountMeta(pubkey=sysvar.SYSVAR_RENT_PUBKEY, is_signer=False, is_writable=False),
],
program_id=SYS_PROGRAM_ID,
data=data,
)
def nonce_advance(params: AdvanceNonceParams) -> TransactionInstruction:
"""Generate an instruction to advance the nonce in a Nonce account.
Args:
params: The advance nonce params
Returns:
The instruction to advance the nonce.
"""
data = SYSTEM_INSTRUCTIONS_LAYOUT.build(
dict(
instruction_type=InstructionType.ADVANCE_NONCE_ACCOUNT,
args={},
)
)
return TransactionInstruction(
keys=[
AccountMeta(pubkey=params.nonce_pubkey, is_signer=False, is_writable=True),
AccountMeta(pubkey=sysvar.SYSVAR_RECENT_BLOCKHASHES_PUBKEY, is_signer=False, is_writable=False),
AccountMeta(pubkey=params.authorized_pubkey, is_signer=True, is_writable=True),
],
program_id=SYS_PROGRAM_ID,
data=data,
)
def nonce_withdraw(params: WithdrawNonceParams) -> TransactionInstruction:
"""Generate an instruction that withdraws lamports from a Nonce account."""
raise NotImplementedError("nonce_withdraw not implemented")
def nonce_authorize(params: AuthorizeNonceParams) -> TransactionInstruction:
"""Generate an instruction that authorizes a new PublicKey as the authority on a Nonce account."""
raise NotImplementedError("nonce_authorize not implemented")
def allocate(params: Union[AllocateParams, AllocateWithSeedParams]) -> TransactionInstruction:
"""Generate an instruction that allocates space in an account without funding.
Args:
params: The allocate params.
Example:
>>> from solana.publickey import PublicKey
>>> allocator = PublicKey(1)
>>> instruction = allocate(
... AllocateParams(account_pubkey=allocator, space=65537)
... )
>>> type(instruction)
<class 'solana.transaction.TransactionInstruction'>
Returns:
The allocate instruction.
"""
if isinstance(params, AllocateWithSeedParams):
data = SYSTEM_INSTRUCTIONS_LAYOUT.build(
dict(
instruction_type=InstructionType.ALLOCATE_WITH_SEED,
args=dict(
base=bytes(params.base_pubkey),
seed=params.seed,
space=params.space,
program_id=bytes(params.program_id),
),
)
)
else:
data = SYSTEM_INSTRUCTIONS_LAYOUT.build(
dict(instruction_type=InstructionType.ALLOCATE, args=dict(space=params.space))
)
return TransactionInstruction(
keys=[
AccountMeta(pubkey=params.account_pubkey, is_signer=True, is_writable=True),
],
program_id=SYS_PROGRAM_ID,
data=data,
)