pub const ORACLE_ACCOUNT_MASM: &str = "use.std::sys\nuse.std::math::u64\nuse.miden::account\nuse.miden::tx\nuse.miden::contracts::auth::basic->auth_tx\n\n# CONSTANTS\n# =================================================================================================\n\n\nconst.MAX_U32=0x0000000100000000\n\n\n#\u{a0}Holds the next storage slot index available. Will be used when we register a publisher,\n# so we can assign it a slot.\nconst.NEXT_PUBLISHER_INDEX_SLOT=0\n\n#\u{a0}Holds the next storage slot index available. Will be used when we register a publisher,\n# so we can assign it a slot.\nconst.PUBLISHER_REGISTRY_MAP_SLOT=1\n\n# The beginning of the storage slots for the publishers.\nconst.PUBLISHERS_STORAGE_SLOT=2\n\n# INTERNAL PROCEDURES (utilities)\n# =================================================================================================\n\n#! Check if the top element of the stack is greater or equal than the second\n#! element on the stack.\n#!\n#! Inputs: [A, B]\n#! Output: [BOOL] 1 if A < B, else 0\nproc.felt_is_lower\n dup \n # => [1, 1, 2]\n movup.2 dup\n # => [2, 2, 1, 1]\n swap.3\n # => [1, 2, 1, 2]\n gt\n # => [1, 2]\nend\n\n#! Calls the PUBLISHER_ID get_entry procedure of the provided account.\n#!\n#! Inputs: [PUBLISHER_ID, PAIR]\n#! Output: [ENTRY]\nproc.call_publisher_get_entry\n push.0x2d1ea4fa1203adfd3c7d00f0961a8d29f459a59768bbe3cf12dcc80b488e1b89\n # => [GET_ENTRY_HASH, PUBLISHER_ID, PAIR]\n swapw swap.2 drop swap.2 drop\n # => [publisher_id,publisher_id, GET_ENTRY_HASH, PAIR]\n \n exec.tx::execute_foreign_procedure\n # => [ENTRY]\nend\n\n#! Swaps two elements stored in the ram at index (i, j).\n#! Input is taken from the stack, example:\n#!\n#! Inputs: [i, j]\n#! Output: []\nproc.ram_swap\n dup\n # => [i, i, j]\n push.0.0.0.0 movup.4 mul.4 mem_loadw\n # => [mem_i, i, j]\n movup.5 dup push.0.0.0.0 movup.4 mul.4 mem_loadw\n # => [mem_j, j, mem_i, i]\n movup.9\n # => [i, mem_j, j, mem_i]\n mul.4 mem_storew dropw mul.4 mem_storew dropw\nend\n\n\n#! Updates the top word on the stack that is an entry to its price\n#! by selecting its 2nd element.\n#! Example:\n#! => [A, 42, B, C]\n#! returns:\n#! => [42]\nproc.entry_to_price\n drop drop swap drop\nend\n\n#! Returns 1 if the element at index i is lower than element at index j.\n#! Example:\n#! => [i, j] for RAM[i] = 42, RAM[i] = 69\n#! returns\n#! => [0, i, j]\nproc.ram_is_lower\n dup push.0.0.0.0 movup.5 mul.4 mem_loadw exec.entry_to_price\n # => [mem_i, i, j]\n movup.2 dup push.0.0.0.0 movup.4 mul.4 mem_loadw exec.entry_to_price\n # => [mem_j, j, mem_i, i]\n movup.2 swap.1 lt\n # => [BOOL, j, i]\n swap.2 movup.2\n # => [BOOL, i, j]\nend\n\n#! Computes the avarage of two elements\n#! Inputs: [a, b]\n#! Output : [avg]\nproc.compute_average\n u32split\n # => [a_high, a_low, b]\n movup.2\n # => [b, a_high, a_low]\n u32split\n # => [b_high, b_low, a_high, a_low]\n exec.u64::wrapping_add\n # => [c_high, c_low]\n push.2.0\n # => [0,2,c_high, c_low]\n exec.u64::div\n # => [avg_high, avg_low]\n push.MAX_U32\n # => [MAX_U32,avg_high, avg_low]\n mul add\n # => [avg]\nend\n#! Sort the N entries in the RAM using a Bubble Sort.\n#! All the elements must already be stored on the RAM and the top of\n#! the stack must include the length of the entries.\n#!\n#! Inputs: [nb_of_entries]\n#! Output: []\nproc.ram_bubble_sort\n # This is the outer counter\n dup\n\n # => [N, N]\n sub.1 dup\n # => [N-1(i), N-1(i), N]\n push.1\n # => [1, N-1(i), N-1(i), N]\n gt\n while.true\n # => [N-1(i), N]\n dup\n # => [N-1(i), N-1(i), N]\n # => Inner index\n push.0\n # => [0(j), N-1(i), N-1(i), N]\n dup \n # => [0(j), 0(j), N-1(i), N-1(i), N]\n swap.2\n # => [N-1(i), 0(j), 0(j), N-1(i), N]\n # We exit this loop if j > i-1\n lte\n # => [bool, N-1(i), 0(j), N]\n while.true\n # => [0(j), N-1(i), N]\n dup\n # => [0(j), 0(j), N-1(i), N]\n add.1\n # => [0(j)+1, 0(j), N-1(i), N]\n swap\n # => [0(j), 0(j)+1, N-1(i), N]\n exec.ram_is_lower\n # => [bool, 0(j), 0(j)+1, N-1(i), N]\n if.true\n else\n dup \n # => [0(j), 0(j), 0(j)+1, N-1(i), N]\n dup.2\n # => [0(j)+1, 0(j), 0(j), 0(j)+1, N-1(i), N]\n swap\n # => [0(j), 0(j)+1, 0(j), 0(j)+1, N-1(i), N]\n exec.ram_swap\n # => [0(j), 0(j)+1, N-1(i), N]\n end\n drop\n # => [0(j)+1, N-1(i), N]\n dup\n # => [0(j)+1, 0(j)+1, N-1(i), N]\n dup.2\n # => [N-1(i), 0(j)+1, 0(j)+1, N-1(i), N]\n lt\n # => [bool, 0(j)+1, N-1(i), N]\n end \n # => [0(j)+1, N-1(i), N]\n drop\n # => [N-1(i), N]\n sub.1\n # => [N-2(i-1), N]\n dup\n # => [N-2(i-1), N-2(i-1), N]\n push.1\n # => [1, N-2(i-1), N-2(i-1), N]\n gte\n # => [bool, N-2(i-1), N]\n end\n drop\nend\n\n\n#! Reads from the sorted entries on the RAM and get the median.\n#! \u{26a0} The RAM must be sorted before!\n#! The input will be the number of elements on top of the stack.\n#! \n#! Inputs: [nb_of_entries]\n#! Output: [median_price]\nproc.ram_get_median\n dup is_odd\n # => [nb_of_entries]\n if.true\n push.0.2.0\n exec.u64::div drop\n # => [index_to_read]\n # = nb_of_entries / 2\n\n push.0.0.0.0 movup.4 mul.4 mem_loadw\n # => [MEDIAN_ENTRY]\n\n exec.entry_to_price\n # => [price_median]\n else\n push.0.2.0\n exec.u64::div drop\n # => [index_to_read]\n \n dup sub.1\n # => [index_to_read - 1, index_to_read]\n\n push.0.0.0.0 movup.4 mul.4 mem_loadw\n # => [ENTRY_N_MINUS_1, index_to_read]\n\n push.0.0.0.0 movup.8 mul.4 mem_loadw\n\n exec.entry_to_price\n # => [price_n, ENTRY_N_MINUS_1]\n\n movdn.4 exec.entry_to_price\n # => [price_n_minus_1, price_n]\n\n exec.compute_average\n end\nend\n\n# EXTERNAL PROCEDURES\n# =================================================================================================\n\n#! Gets entry from the oracle\'s data slots.\n#!\n#! Inputs: [PUBLISHER_ID, PAIR]\n#! Outputs: [ENTRY]\nexport.get_entry\n # Verifies if the publisher is registered, panics if not\n # dupw push.PUBLISHER_REGISTRY_MAP_SLOT exec.account::get_map_item dropw\n # => [PUBLISHER_ID, PAIR]\n\n # Push the get_entry hash function for the publisher account\n exec.call_publisher_get_entry\n\n # Truncate if necessary\n exec.sys::truncate_stack\nend\n\n#! Gets the median price of a given asset.\n#!\n#! Inputs: [PAIR]\n#! Outputs: [median_price]\nexport.get_median\n # Iterate from 3 to NEXT_PUBLISHER_INDEX_SLOT value.\n push.0.0.NEXT_PUBLISHER_INDEX_SLOT exec.account::get_item drop drop drop\n # => [next_publisher_slot, 0, 0, PAIR]\n\n push.PUBLISHERS_STORAGE_SLOT exec.felt_is_lower\n # => [PUBLISHERS_STORAGE_SLOT, next_publisher_slot, 0, 0, PAIR]\n\n while.true\n # Get the publisher id at slot [top of the stack]\n dup exec.account::get_item\n # => [PUBLISHER_ID, PUBLISHERS_STORAGE_SLOT, next_publisher_slot, 0, 0, PAIR]\n\n dupw.2 swapw.1\n # => [PUBLISHER_ID, PAIR, PUBLISHERS_STORAGE_SLOT, next_publisher_slot, 0, 0, PAIR]\n\n # Call get_entry\n exec.call_publisher_get_entry\n # => [ENTRY, PUBLISHERS_STORAGE_SLOT, next_publisher_slot, PAIR]\n \n # Store the entry in the RAM from index 0 to index N\n dup.4 push.PUBLISHERS_STORAGE_SLOT sub mul.4 # we multiplied the result by 4 because se can store with % 4!=0 addresses\n mem_storew dropw\n # => [PUBLISHERS_STORAGE_SLOT, next_publisher_slot, PAIR]\n # Increment the next index and check if there\'s still publishers to process\n add.1 exec.felt_is_lower\n # => [PUBLISHERS_STORAGE_SLOT +1 , next_publisher_slot, PAIR]\n end\n # Drop the utilities used to get all the entries - only keep the length on the ram.\n\n swap drop movdn.4 dropw\n # => [4]\n \n push.PUBLISHERS_STORAGE_SLOT\n #\u{a0}=> [PUBLISHER_STORAGE_SLOT, 4]\n\n sub \n # => [Number of entries]\n # Sort the entries stored on the RAM\n exec.ram_bubble_sort\n # => [4]\n \n # Retrieves the median from the sorted RAM entries\n exec.ram_get_median\n # => [median_price]\n\n\n\n exec.sys::truncate_stack\nend\n\n#! Registers a new publishers into the Oracle.\n#! Can only be called by the Owner of the Oracle account.\n#! Will reserve a storage slot for the publisher if it\'s not already registered,\n#! which mean this publisher will be able to publish data.\n#!\n#! Inputs: [PUBLISHER_ID]\n#! Outputs: []\nexport.register_publisher\n # Check if it is not already present in the registry, if yes raise err (get_map_item)\n # TODO: How?\n\n # Duplicate the publisher id\n dupw\n # => [PUBLISHER_ID, PUBLISHER_ID]\n\n # Retrieve the next_publisher_slot available from the slot\n push.NEXT_PUBLISHER_INDEX_SLOT exec.account::get_item\n\n # => [NEXT_PUBLISHER_SLOT, PUBLISHER_ID, PUBLISHER_ID]\n\n # Prepare the stack for the set_item and set_map_item calls\n dupw movdnw.2 drop drop drop\n # => [next_publisher_slot, PUBLISHER_ID, NEXT_PUBLISHER_SLOT, PUBLISHER_ID]\n\n # Store the publisher into its assigned slot\n exec.account::set_item dropw dropw swapw\n # => [PUBLISHER_ID, NEXT_PUBLISHER_SLOT]\n\n # Register the publisher into its mapping\n push.PUBLISHER_REGISTRY_MAP_SLOT exec.account::set_map_item\n # => []\n\n # Increment NEXT_PUBLISHER_INDEX_SLOT\n push.NEXT_PUBLISHER_INDEX_SLOT exec.account::get_item\n # => [PUBLISHER_INDEX_SLOT]\n\n # Update the storage value\n swap.3 add.1 swap.3\n # => [PUBLISHER_INDEX_SLOT+1]\n\n push.NEXT_PUBLISHER_INDEX_SLOT exec.account::set_item\n # => []\n\n # Only the oracle owner should be able to call this\n call.auth_tx::auth_tx_rpo_falcon512\n drop\n\n exec.sys::truncate_stack\nend\n";