[sui 4/x] - governance modules, update to sui `0.29.0` (#732)
* state getters and setters, change Move.toml dependency to sui/integration_v2 * finish state.move * add new line to pyth * use deployer cap pattern for state module * sui pyth * update price feeds, dynamic object fields, Sui object PriceInfoObject * register price info object with pyth state after creation * sui governance * some newlines
This commit is contained in:
parent
3e5600508b
commit
2bbeb03ca9
|
@ -0,0 +1,12 @@
|
|||
module pyth::contract_upgrade {
|
||||
use pyth::state::{State};
|
||||
|
||||
use wormhole::state::{State as WormState};
|
||||
|
||||
friend pyth::governance;
|
||||
|
||||
/// Payload should be the bytes digest of the new contract.
|
||||
public(friend) fun execute(_worm_state: &WormState, _pyth_state: &State, _payload: vector<u8>){
|
||||
// TODO
|
||||
}
|
||||
}
|
|
@ -0,0 +1,73 @@
|
|||
module pyth::governance {
|
||||
use sui::tx_context::{TxContext};
|
||||
|
||||
use pyth::data_source::{Self};
|
||||
use pyth::governance_instruction;
|
||||
use pyth::governance_action;
|
||||
use pyth::contract_upgrade;
|
||||
use pyth::set_governance_data_source;
|
||||
use pyth::set_data_sources;
|
||||
use pyth::set_stale_price_threshold;
|
||||
use pyth::state::{State};
|
||||
use pyth::set_update_fee;
|
||||
use pyth::state;
|
||||
|
||||
use wormhole::vaa::{Self, VAA};
|
||||
use wormhole::state::{State as WormState};
|
||||
|
||||
public entry fun execute_governance_instruction(
|
||||
pyth_state : &mut State,
|
||||
worm_state: &WormState,
|
||||
vaa_bytes: vector<u8>,
|
||||
ctx: &mut TxContext
|
||||
) {
|
||||
let parsed_vaa = parse_and_verify_governance_vaa(pyth_state, worm_state, vaa_bytes, ctx);
|
||||
let instruction = governance_instruction::from_byte_vec(vaa::take_payload(parsed_vaa));
|
||||
|
||||
// Dispatch the instruction to the appropiate handler
|
||||
let action = governance_instruction::get_action(&instruction);
|
||||
if (action == governance_action::new_contract_upgrade()) {
|
||||
assert!(governance_instruction::get_target_chain_id(&instruction) != 0,
|
||||
0); // TODO - error::governance_contract_upgrade_chain_id_zero()
|
||||
contract_upgrade::execute(worm_state, pyth_state, governance_instruction::destroy(instruction));
|
||||
} else if (action == governance_action::new_set_governance_data_source()) {
|
||||
set_governance_data_source::execute(pyth_state, governance_instruction::destroy(instruction));
|
||||
} else if (action == governance_action::new_set_data_sources()) {
|
||||
set_data_sources::execute(pyth_state, governance_instruction::destroy(instruction));
|
||||
} else if (action == governance_action::new_set_update_fee()) {
|
||||
set_update_fee::execute(pyth_state, governance_instruction::destroy(instruction));
|
||||
} else if (action == governance_action::new_set_stale_price_threshold()) {
|
||||
set_stale_price_threshold::execute(pyth_state, governance_instruction::destroy(instruction));
|
||||
} else {
|
||||
governance_instruction::destroy(instruction);
|
||||
assert!(false, 0); // TODO - error::invalid_governance_action()
|
||||
}
|
||||
}
|
||||
|
||||
fun parse_and_verify_governance_vaa(
|
||||
pyth_state: &mut State,
|
||||
worm_state: &WormState,
|
||||
bytes: vector<u8>,
|
||||
ctx: &mut TxContext
|
||||
): VAA {
|
||||
let parsed_vaa = vaa::parse_and_verify(worm_state, bytes, ctx);
|
||||
|
||||
// Check that the governance data source is valid
|
||||
assert!(
|
||||
state::is_valid_governance_data_source(
|
||||
pyth_state,
|
||||
data_source::new(
|
||||
(vaa::emitter_chain(&parsed_vaa) as u64),
|
||||
vaa::emitter_address(&parsed_vaa))),
|
||||
0); // TODO - error::invalid_governance_data_source()
|
||||
|
||||
// Check that the sequence number is greater than the last executed governance VAA
|
||||
let sequence = vaa::sequence(&parsed_vaa);
|
||||
assert!(sequence > state::get_last_executed_governance_sequence(pyth_state), 0); // TODO - error::invalid_governance_sequence_number()
|
||||
state::set_last_executed_governance_sequence(pyth_state, sequence);
|
||||
|
||||
parsed_vaa
|
||||
}
|
||||
}
|
||||
|
||||
// TODO - add tests
|
|
@ -0,0 +1,38 @@
|
|||
module pyth::governance_action {
|
||||
//use pyth::error;
|
||||
|
||||
const CONTRACT_UPGRADE: u8 = 0;
|
||||
const SET_GOVERNANCE_DATA_SOURCE: u8 = 1;
|
||||
const SET_DATA_SOURCES: u8 = 2;
|
||||
const SET_UPDATE_FEE: u8 = 3;
|
||||
const SET_STALE_PRICE_THRESHOLD: u8 = 4;
|
||||
|
||||
struct GovernanceAction has copy, drop {
|
||||
value: u8,
|
||||
}
|
||||
|
||||
public fun from_u8(value: u8): GovernanceAction {
|
||||
assert!(CONTRACT_UPGRADE <= value && value <= SET_STALE_PRICE_THRESHOLD, 0); //TODO - add specific error: error::invalid_governance_action()
|
||||
GovernanceAction { value }
|
||||
}
|
||||
|
||||
public fun new_contract_upgrade(): GovernanceAction {
|
||||
GovernanceAction { value: CONTRACT_UPGRADE }
|
||||
}
|
||||
|
||||
public fun new_set_governance_data_source(): GovernanceAction {
|
||||
GovernanceAction { value: SET_GOVERNANCE_DATA_SOURCE }
|
||||
}
|
||||
|
||||
public fun new_set_data_sources(): GovernanceAction {
|
||||
GovernanceAction { value: SET_DATA_SOURCES }
|
||||
}
|
||||
|
||||
public fun new_set_update_fee(): GovernanceAction {
|
||||
GovernanceAction { value: SET_UPDATE_FEE }
|
||||
}
|
||||
|
||||
public fun new_set_stale_price_threshold(): GovernanceAction {
|
||||
GovernanceAction { value: SET_STALE_PRICE_THRESHOLD }
|
||||
}
|
||||
}
|
|
@ -0,0 +1,84 @@
|
|||
module pyth::governance_instruction {
|
||||
use wormhole::cursor;
|
||||
use pyth::deserialize;
|
||||
use pyth::governance_action::{Self, GovernanceAction};
|
||||
|
||||
const MAGIC: vector<u8> = x"5054474d"; // "PTGM": Pyth Governance Message
|
||||
const MODULE: u8 = 1;
|
||||
|
||||
struct GovernanceInstruction {
|
||||
module_: u8,
|
||||
action: GovernanceAction,
|
||||
target_chain_id: u64,
|
||||
payload: vector<u8>,
|
||||
}
|
||||
|
||||
fun validate(instruction: &GovernanceInstruction) {
|
||||
assert!(instruction.module_ == MODULE, 0); // TODO - add custom error::invalid_governance_module()
|
||||
let target_chain_id = instruction.target_chain_id;
|
||||
assert!(target_chain_id == (wormhole::state::chain_id() as u64) || target_chain_id == 0, 0); // TODO - custom error: error::invalid_governance_target_chain_id()
|
||||
}
|
||||
|
||||
public fun from_byte_vec(bytes: vector<u8>): GovernanceInstruction {
|
||||
let cursor = cursor::new(bytes);
|
||||
let magic = deserialize::deserialize_vector(&mut cursor, 4);
|
||||
assert!(magic == MAGIC, 0); // TODO error::invalid_governance_magic_value()
|
||||
let module_ = deserialize::deserialize_u8(&mut cursor);
|
||||
let action = governance_action::from_u8(deserialize::deserialize_u8(&mut cursor));
|
||||
let target_chain_id = deserialize::deserialize_u16(&mut cursor);
|
||||
let payload = cursor::take_rest(cursor);
|
||||
|
||||
let instruction = GovernanceInstruction {
|
||||
module_,
|
||||
action,
|
||||
target_chain_id : (target_chain_id as u64),
|
||||
payload
|
||||
};
|
||||
validate(&instruction);
|
||||
|
||||
instruction
|
||||
}
|
||||
|
||||
public fun get_module(instruction: &GovernanceInstruction): u8 {
|
||||
instruction.module_
|
||||
}
|
||||
|
||||
public fun get_action(instruction: &GovernanceInstruction): GovernanceAction {
|
||||
instruction.action
|
||||
}
|
||||
|
||||
public fun get_target_chain_id(instruction: &GovernanceInstruction): u64 {
|
||||
instruction.target_chain_id
|
||||
}
|
||||
|
||||
public fun destroy(instruction: GovernanceInstruction): vector<u8> {
|
||||
let GovernanceInstruction {
|
||||
module_: _,
|
||||
action: _,
|
||||
target_chain_id: _,
|
||||
payload: payload
|
||||
} = instruction;
|
||||
payload
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[expected_failure]
|
||||
fun test_from_byte_vec_invalid_magic() {
|
||||
let bytes = x"5054474eb01087a85361f738f19454e66664d3c9";
|
||||
destroy(from_byte_vec(bytes));
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[expected_failure]
|
||||
fun test_from_byte_vec_invalid_module() {
|
||||
let bytes = x"5054474db00187a85361f738f19454e66664d3c9";
|
||||
destroy(from_byte_vec(bytes));
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[expected_failure]
|
||||
fun test_from_byte_vec_invalid_target_chain_id() {
|
||||
let bytes = x"5054474db00187a85361f738f19454e66664d3c9";
|
||||
destroy(from_byte_vec(bytes));
|
||||
}
|
||||
}
|
|
@ -0,0 +1,43 @@
|
|||
module pyth::set_data_sources {
|
||||
use std::vector;
|
||||
|
||||
use wormhole::cursor;
|
||||
use wormhole::external_address::{Self};
|
||||
|
||||
use pyth::deserialize;
|
||||
use pyth::data_source::{Self, DataSource};
|
||||
use pyth::state::{Self, State};
|
||||
|
||||
friend pyth::governance;
|
||||
|
||||
struct SetDataSources {
|
||||
sources: vector<DataSource>,
|
||||
}
|
||||
|
||||
public(friend) fun execute(state: &mut State, payload: vector<u8>) {
|
||||
let SetDataSources { sources } = from_byte_vec(payload);
|
||||
state::set_data_sources(state, sources);
|
||||
}
|
||||
|
||||
fun from_byte_vec(bytes: vector<u8>): SetDataSources {
|
||||
let cursor = cursor::new(bytes);
|
||||
let data_sources_count = deserialize::deserialize_u8(&mut cursor);
|
||||
|
||||
let sources = vector::empty();
|
||||
|
||||
let i = 0;
|
||||
while (i < data_sources_count) {
|
||||
let emitter_chain_id = deserialize::deserialize_u16(&mut cursor);
|
||||
let emitter_address = external_address::from_bytes(deserialize::deserialize_vector(&mut cursor, 32));
|
||||
vector::push_back(&mut sources, data_source::new((emitter_chain_id as u64), emitter_address));
|
||||
|
||||
i = i + 1;
|
||||
};
|
||||
|
||||
cursor::destroy_empty(cursor);
|
||||
|
||||
SetDataSources {
|
||||
sources
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,36 @@
|
|||
module pyth::set_governance_data_source {
|
||||
use pyth::deserialize;
|
||||
use pyth::data_source;
|
||||
use pyth::state::{Self, State};
|
||||
|
||||
use wormhole::cursor;
|
||||
use wormhole::external_address::{Self, ExternalAddress};
|
||||
//use wormhole::state::{Self}
|
||||
|
||||
friend pyth::governance;
|
||||
|
||||
struct SetGovernanceDataSource {
|
||||
emitter_chain_id: u64,
|
||||
emitter_address: ExternalAddress,
|
||||
initial_sequence: u64,
|
||||
}
|
||||
|
||||
public(friend) fun execute(pyth_state: &mut State, payload: vector<u8>) {
|
||||
let SetGovernanceDataSource { emitter_chain_id, emitter_address, initial_sequence } = from_byte_vec(payload);
|
||||
state::set_governance_data_source(pyth_state, data_source::new(emitter_chain_id, emitter_address));
|
||||
state::set_last_executed_governance_sequence(pyth_state, initial_sequence);
|
||||
}
|
||||
|
||||
fun from_byte_vec(bytes: vector<u8>): SetGovernanceDataSource {
|
||||
let cursor = cursor::new(bytes);
|
||||
let emitter_chain_id = deserialize::deserialize_u16(&mut cursor);
|
||||
let emitter_address = external_address::from_bytes(deserialize::deserialize_vector(&mut cursor, 32));
|
||||
let initial_sequence = deserialize::deserialize_u64(&mut cursor);
|
||||
cursor::destroy_empty(cursor);
|
||||
SetGovernanceDataSource {
|
||||
emitter_chain_id: (emitter_chain_id as u64),
|
||||
emitter_address,
|
||||
initial_sequence
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,25 @@
|
|||
module pyth::set_stale_price_threshold {
|
||||
use wormhole::cursor;
|
||||
use pyth::deserialize;
|
||||
use pyth::state::{Self, State};
|
||||
|
||||
friend pyth::governance;
|
||||
|
||||
struct SetStalePriceThreshold {
|
||||
threshold: u64,
|
||||
}
|
||||
|
||||
public(friend) fun execute(state: &mut State, payload: vector<u8>) {
|
||||
let SetStalePriceThreshold { threshold } = from_byte_vec(payload);
|
||||
state::set_stale_price_threshold_secs(state, threshold);
|
||||
}
|
||||
|
||||
fun from_byte_vec(bytes: vector<u8>): SetStalePriceThreshold {
|
||||
let cursor = cursor::new(bytes);
|
||||
let threshold = deserialize::deserialize_u64(&mut cursor);
|
||||
cursor::destroy_empty(cursor);
|
||||
SetStalePriceThreshold {
|
||||
threshold
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,40 @@
|
|||
module pyth::set_update_fee {
|
||||
use sui::math::{Self};
|
||||
|
||||
use pyth::deserialize;
|
||||
use pyth::state::{Self, State};
|
||||
|
||||
use wormhole::cursor;
|
||||
|
||||
|
||||
friend pyth::governance;
|
||||
|
||||
const MAX_U64: u128 = (1 << 64) - 1;
|
||||
|
||||
struct SetUpdateFee {
|
||||
mantissa: u64,
|
||||
exponent: u64,
|
||||
}
|
||||
|
||||
public(friend) fun execute(pyth_state: &mut State, payload: vector<u8>) {
|
||||
let SetUpdateFee { mantissa, exponent } = from_byte_vec(payload);
|
||||
assert!(exponent <= 255, 0); // TODO - throw error that exponent does not fit in a u8
|
||||
let fee = apply_exponent(mantissa, (exponent as u8));
|
||||
state::set_base_update_fee(pyth_state, fee);
|
||||
}
|
||||
|
||||
fun from_byte_vec(bytes: vector<u8>): SetUpdateFee {
|
||||
let cursor = cursor::new(bytes);
|
||||
let mantissa = deserialize::deserialize_u64(&mut cursor);
|
||||
let exponent = deserialize::deserialize_u64(&mut cursor);
|
||||
cursor::destroy_empty(cursor);
|
||||
SetUpdateFee {
|
||||
mantissa,
|
||||
exponent,
|
||||
}
|
||||
}
|
||||
|
||||
fun apply_exponent(mantissa: u64, exponent: u8): u64 {
|
||||
mantissa * math::pow(10, exponent)
|
||||
}
|
||||
}
|
|
@ -9,6 +9,12 @@ module pyth::state {
|
|||
use pyth::price_identifier::{PriceIdentifier};
|
||||
|
||||
friend pyth::pyth;
|
||||
friend pyth::governance_action;
|
||||
friend pyth::set_update_fee;
|
||||
friend pyth::set_stale_price_threshold;
|
||||
friend pyth::set_data_sources;
|
||||
friend pyth::governance;
|
||||
friend pyth::set_governance_data_source;
|
||||
|
||||
/// Capability for creating a bridge state object, granted to sender when this
|
||||
/// module is deployed
|
||||
|
|
Loading…
Reference in New Issue