#pragma version 5 // ================================================================================================ // PriceKeeper Approval Program // ================================================================================================ // // App-globals: // sym : byte[] Symbol to keep price for // vaddr : byte[] Validator account // price : uint64 current price // stdev : uint64 current confidence (standard deviation) // slot : uint64 slot of this onchain publication // exp : byte[] exponent. Interpret as two-compliment, Big-Endian 64bit // ts : uint64 last timestamp // // Slots: // 0 Input message block // 1 SHA256-Hashed message // // The Message format must have the packed fields: // // Field size // 9 header Literal "PRICEDATA" // 1 version int8 (Must be 1) // 8 dest This appId // 16 symbol String filled with spaces e.g ("ALGO/USD ") // 8 price Price. 64bit integer. // 8 priceexp Price exponent. Interpret as two-compliment, Big-Endian 64bit // 8 conf Confidence (stdev). 64bit integer. // 8 slot Valid-slot of this aggregate price. // 8 ts timestamp of this price submitted by PriceFetcher service // 32 s Signature s-component // 32 r Signature r-component // // Size: 138 bytes. // // ------------------------------------------------------------------------------------------------ // Application creation. int 0 txn ApplicationID == bnz handle_create // Handle app call: send price message txn OnCompletion int NoOp == bnz handle_call // Handle deletion. txn OnCompletion int DeleteApplication == bnz success // Fail otherwise err handle_create: // ----------------------------------------------------- // Handle creation // Arg 0: Validator address // Arg 1: Symbol to keep price data // ----------------------------------------------------- byte "vaddr" txn ApplicationArgs 0 app_global_put byte "sym" txn ApplicationArgs 1 dup len int 16 == assert app_global_put byte "ts" global LatestTimestamp app_global_put b success // ----------------------------------------------------- // Handle app call // ----------------------------------------------------- handle_call: // Group size must be 4 to raise computational allowance to 2800 global GroupSize int 4 == assert // if this is one of dummy transactions(0, 1 or 2), exit with success txn GroupIndex int 3 != bnz success // Verify if sender is the data validator txn Sender byte "vaddr" app_global_get == assert // Retrieve message, store in slot 0 txn ApplicationArgs 0 store 0 // ------------------------------------------------------ // Validate message // ------------------------------------------------------ // Check length load 0 len int 138 == assert // Check header byte "PRICEDATA" load 0 extract 0 9 == assert // Check version - must be 1. load 0 extract 9 1 byte 0x01 == assert // Check destination - must be this appId. load 0 extract 10 8 btoi txn ApplicationID == assert // Reject zero price load 0 extract 34 8 btoi int 0 != assert // Reject zero slot load 0 extract 58 8 btoi int 0 != assert // Check timestamp: // * must be higher than ts recorded in global state // * must be lower than current block stamp + 10s // (TODO: check this again) load 0 extract 66 8 btoi dup dup global LatestTimestamp int 10 + < assert byte "ts" app_global_get > assert // ed25519verify args in stack: // data (hash of message) load 0 extract 0 74 sha512_256 // (B) signature load 0 extract 74 64 // validator-address byte "vaddr" app_global_get // Verify signature ed25519verify int 1 == assert // ---------------------------------------------------------------------------- // Verified. Store data to app globals. // ---------------------------------------------------------------------------- byte "ts" load 0 extract 66 8 btoi app_global_put byte "price" load 0 extract 34 8 btoi app_global_put byte "exp" load 0 extract 42 8 app_global_put byte "conf" load 0 extract 50 8 btoi app_global_put byte "slot" load 0 extract 58 8 btoi app_global_put b success // ---------------------------------------------------------------------------- fail: int 0 return success: int 1 return