Skip to content

update mapping account size, add support for update price #48

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 1 commit into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
28 changes: 26 additions & 2 deletions program_admin/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -114,7 +114,7 @@ async def fetch_minimum_balance(self, size: int) -> int:

async def refresh_program_accounts(self):
async with AsyncClient(self.rpc_endpoint) as client:
logger.info("Refreshing program accounts")
logger.info(f"Refreshing program accounts for {self.program_key}")
result = (
await client.get_program_accounts(
pubkey=self.program_key,
Expand Down Expand Up @@ -395,7 +395,9 @@ async def sync_mapping_instructions(
)
)

logger.debug("Building pyth_program.init_mapping instruction")
logger.debug(
f"Building pyth_program.init_mapping instruction: {funding_keypair.public_key}, {mapping_0_keypair.public_key}"
)
instructions.append(
pyth_program.init_mapping(
self.program_key,
Expand Down Expand Up @@ -603,6 +605,28 @@ async def sync_price_instructions(

return (instructions, [funding_keypair, price_keypair])

def update_price_instructions(
self,
publisher_keypair: Keypair,
price_pubkey: PublicKey,
price: int,
confidence: int,
price_slot: int,
) -> Tuple[List[TransactionInstruction], List[Keypair]]:
instructions = []
instructions.append(
pyth_program.upd_price(
self.program_key,
publisher_keypair.public_key,
price_pubkey,
1,
price,
confidence,
price_slot,
)
)
return (instructions, [publisher_keypair])

async def sync_authority_permissions_instructions(
self,
reference_authority_permissions: ReferenceAuthorityPermissions,
Expand Down
48 changes: 45 additions & 3 deletions program_admin/cli.py
Original file line number Diff line number Diff line change
Expand Up @@ -297,23 +297,31 @@ def list_accounts(network, rpc_endpoint, program_key, keys, publishers, commitme

for product_key in mapping_account.data.product_account_keys:
product_account = program_admin.get_product_account(product_key)
print(f" Product Public Key: {product_account.public_key}")
print(f" Product: {product_account.data.metadata['symbol']}")

if product_account.data.first_price_account_key != PublicKey(0):
price_account = program_admin.get_price_account(
product_account.data.first_price_account_key
)
print(f" Price Account: {price_account.public_key}")
print(
f" Price: {price_account.data.exponent} exponent ({price_account.data.components_count} components)"
)
print(f" numPublishers: {price_account.data.components_count}")
print(f" numPrices: {price_account.data.quoters_count}")
print(f" numComponents: {len(price_account.data.price_components)}")
print(f" Aggregate: {price_account.data.aggregate}")

for component in price_account.data.price_components:
try:
name = publishers_map["names"][component.publisher_key]
except KeyError:
name = f"??? ({component.publisher_key})"

print(f" Publisher: {name}")
print(
f" Publisher: {name}: {component.latest_price} {component.aggregate_price}"
)

mapping_key = mapping_account.data.next_mapping_account_key

Expand Down Expand Up @@ -462,11 +470,9 @@ def sync(
ref_permissions = parse_permissions_with_overrides(
Path(permissions), Path(overrides), network
)

ref_authority_permissions = parse_authority_permissions_json(
Path(authority_permissions)
)

asyncio.run(
program_admin.sync(
ref_products=ref_products,
Expand All @@ -480,6 +486,41 @@ def sync(
)


@click.command()
@click.option("--network", help="Solana network", envvar="NETWORK")
@click.option("--rpc-endpoint", help="Solana RPC endpoint", envvar="RPC_ENDPOINT")
@click.option("--program-key", help="Pyth program key", envvar="PROGRAM_KEY")
@click.option("--keys", help="Path to keys directory", envvar="KEYS")
@click.option("--publisher", help="key file of the publisher")
@click.option("--price-account", help="Public key of the price account")
@click.option("--price", help="Price to set")
@click.option("--price-slot", help="Price slot to set")
def update_price(
network,
rpc_endpoint,
program_key,
keys,
publisher,
price_account,
price,
price_slot,
):
program_admin = ProgramAdmin(
network=network,
rpc_endpoint=rpc_endpoint,
key_dir=keys,
program_key=program_key,
price_store_key=None,
commitment="confirmed",
)
publisher_keypair = load_keypair(publisher, key_dir=keys)
price_account = PublicKey(price_account)
(instructions, signers,) = program_admin.update_price_instructions(
publisher_keypair, price_account, int(price), 100, int(price_slot)
)
asyncio.run(program_admin.send_transaction(instructions, signers))


@click.command()
@click.option("--network", help="Solana network", envvar="NETWORK")
@click.option("--rpc-endpoint", help="Solana RPC endpoint", envvar="RPC_ENDPOINT")
Expand Down Expand Up @@ -578,5 +619,6 @@ def resize_price_accounts_v2(
cli.add_command(update_product_metadata)
cli.add_command(migrate_upgrade_authority)
cli.add_command(resize_price_accounts_v2)
cli.add_command(update_price)
logger.remove()
logger.add(sys.stdout, serialize=(not os.environ.get("DEV_MODE")))
78 changes: 77 additions & 1 deletion program_admin/instructions.py
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
from typing import Dict

from construct import Bytes, Int32sl, Int32ul, Struct
from construct import Bytes, Int32sl, Int32ul, Int64sl, Struct
from solana.publickey import PublicKey
from solana.system_program import SYS_PROGRAM_ID
from solana.sysvar import SYSVAR_CLOCK_PUBKEY
from solana.transaction import AccountMeta, TransactionInstruction

from program_admin.types import ReferenceAuthorityPermissions
Expand All @@ -16,6 +17,8 @@
COMMAND_ADD_PRICE = 4
COMMAND_ADD_PUBLISHER = 5
COMMAND_DEL_PUBLISHER = 6
COMMAND_UPD_PRICE = 7
COMMAND_INIT_PRICE = 9
COMMAND_MIN_PUBLISHERS = 12
COMMAND_RESIZE_PRICE_ACCOUNT = 14
COMMAND_DEL_PRICE = 15
Expand Down Expand Up @@ -297,6 +300,79 @@ def toggle_publisher(
)


def init_price(
program_key: PublicKey,
funding_key: PublicKey,
price_account_key: PublicKey,
) -> TransactionInstruction:
layout = Struct(
"version" / Int32ul, "command" / Int32sl, "exponent" / Int32sl, "type" / Int32ul
)
data = layout.build(
dict(
version=PROGRAM_VERSION,
command=COMMAND_INIT_PRICE,
exponent=8,
type=PRICE_TYPE_PRICE,
)
)

permissions_account = get_permissions_account(
program_key, AUTHORITY_PERMISSIONS_PDA_SEED
)

return TransactionInstruction(
data=data,
keys=[
AccountMeta(pubkey=funding_key, is_signer=True, is_writable=True),
AccountMeta(pubkey=price_account_key, is_signer=True, is_writable=True),
AccountMeta(pubkey=permissions_account, is_signer=False, is_writable=True),
],
program_id=program_key,
)


def upd_price(
program_key: PublicKey,
funding_key: PublicKey,
price_account_key: PublicKey,
status: int,
price: int,
confidence: int,
publish_slot: int,
) -> TransactionInstruction:
layout = Struct(
"version" / Int32ul,
"command" / Int32sl,
"status" / Int32sl,
"unused" / Int32sl,
"price" / Int64sl,
"confidence" / Int64sl,
"publish_slot" / Int64sl,
)
data = layout.build(
dict(
version=PROGRAM_VERSION,
command=COMMAND_UPD_PRICE,
unused=0,
status=status,
price=price,
confidence=confidence,
publish_slot=publish_slot,
)
)

return TransactionInstruction(
data=data,
keys=[
AccountMeta(pubkey=funding_key, is_signer=True, is_writable=True),
AccountMeta(pubkey=price_account_key, is_signer=False, is_writable=True),
AccountMeta(pubkey=SYSVAR_CLOCK_PUBKEY, is_signer=False, is_writable=False),
],
program_id=program_key,
)


def upd_permissions(
program_key: PublicKey,
upgrade_authority: PublicKey,
Expand Down
1 change: 0 additions & 1 deletion program_admin/keys.py
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,6 @@ def load_keypair(
return Keypair.from_secret_key(data)
else:
file_path = Path(key_dir) / f"{label_or_pubkey}.json"

if not file_path.exists():
if generate:
return generate_keypair(label_or_pubkey, key_dir)
Expand Down
4 changes: 2 additions & 2 deletions program_admin/util.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,8 +14,8 @@
ReferencePermissions,
)

MAPPING_ACCOUNT_SIZE = 20536 # https://github.com/pyth-network/pyth-client/blob/b49f73afe32ce8685a3d05e32d8f3bb51909b061/program/src/oracle/oracle.h#L88
MAPPING_ACCOUNT_PRODUCT_LIMIT = 640
MAPPING_ACCOUNT_SIZE = 160056 # https://github.com/pyth-network/pyth-client/blob/main/program/c/src/oracle/oracle.h#L120
MAPPING_ACCOUNT_PRODUCT_LIMIT = 5000
PRICE_ACCOUNT_V1_SIZE = 3312
PRICE_ACCOUNT_V2_SIZE = 12576
PRICE_V1_COMP_COUNT = 32
Expand Down
Loading