diff --git a/.github/workflows/run-unit-tests.js.yml b/.github/workflows/run-unit-tests.js.yml new file mode 100644 index 0000000..3df3522 --- /dev/null +++ b/.github/workflows/run-unit-tests.js.yml @@ -0,0 +1,29 @@ +# This workflow will do a clean installation of node dependencies, cache/restore them, build the source code and run tests across different versions of node +# For more information see: https://docs.github.com/en/actions/automating-builds-and-tests/building-and-testing-nodejs + +name: Contracts unit tests + +on: + push: + branches: [ "main" ] + pull_request: + branches: [ "main" ] + +jobs: + build: + runs-on: ubuntu-latest + + strategy: + matrix: + node-version: [18.x] + # See supported Node.js release schedule at https://nodejs.org/en/about/releases/ + + steps: + - uses: actions/checkout@v3 + - name: Use Node.js ${{ matrix.node-version }} + uses: actions/setup-node@v3 + with: + node-version: ${{ matrix.node-version }} + cache: 'npm' + - run: npm ci + - run: npm test diff --git a/Clarinet.toml b/Clarinet.toml index c700b0c..0dc7b08 100644 --- a/Clarinet.toml +++ b/Clarinet.toml @@ -6,8 +6,8 @@ telemetry = false cache_dir = './.cache' requirements = [] -[contracts.pyth-oracle-dev-preview-1] -path = 'contracts/pyth-oracle-dev-preview-1.clar' +[contracts.pyth-governance-v1] +path = 'contracts/pyth-governance-v1.clar' clarity_version = 2 epoch = 2.4 @@ -16,33 +16,39 @@ path = 'contracts/pyth-oracle-v1.clar' clarity_version = 2 epoch = 2.4 -[contracts.wormhole-core-dev-preview-1] -path = 'contracts/deps/wormhole-core-dev-preview-1.clar' +[contracts.pyth-store-v1] +path = 'contracts/pyth-store-v1.clar' clarity_version = 2 epoch = 2.4 -[contracts.pyth-oracle-trait] -path = 'contracts/pyth-oracle-trait.clar' + +[contracts.pyth-pnau-decoder-v1] +path = 'contracts/pyth-pnau-decoder-v1.clar' clarity_version = 2 epoch = 2.4 -[contracts.wormhole-core-trait] -path = 'contracts/deps/wormhole-core-trait.clar' +[contracts.pyth-traits-v1] +path = 'contracts/pyth-traits-v1.clar' clarity_version = 2 epoch = 2.4 -[contracts.hk-cursor-v1] -path = 'contracts/deps/hk-cursor-v1.clar' +[contracts.wormhole-core-v1] +path = 'contracts/wormhole/wormhole-core-v1.clar' +clarity_version = 2 +epoch = 2.4 + +[contracts.wormhole-traits-v1] +path = 'contracts/wormhole/wormhole-traits-v1.clar' clarity_version = 2 epoch = 2.4 -[contracts.hk-cursor-v2] -path = 'contracts/deps/hk-cursor-v2.clar' +[contracts.hk-cursor-v1] +path = 'contracts/hiro-kit/hk-cursor-v1.clar' clarity_version = 2 epoch = 2.4 [contracts.hk-merkle-tree-keccak160-v1] -path = 'contracts/deps/hk-merkle-tree-keccak160-v1.clar' +path = 'contracts/hiro-kit/hk-merkle-tree-keccak160-v1.clar' clarity_version = 2 epoch = 2.4 diff --git a/README.md b/README.md index dcbc96a..1aff7cc 100644 --- a/README.md +++ b/README.md @@ -183,10 +183,8 @@ These events can be observed using [Chainhook](https://github.com/hirosystems/ch ## Todos: -- [] Support for `PNAU` wire format -- [] Resolve todos, mostly aiming at adding more checks in both wormhole and pyth contracts -- [] Address insufficient test coverage -- [] Add a method to update the list of price feeds watched -- [] Resolve build warnings -- [] Improve documentation -- [] Add example +- [ ] Resolve todos, mostly aiming at adding more checks in both wormhole and pyth contracts +- [ ] Address insufficient test coverage +- [ ] Resolve build warnings +- [ ] Improve documentation +- [ ] Add example diff --git a/contracts/deps/hk-cursor-v1.clar b/contracts/deps/hk-cursor-v1.clar deleted file mode 100644 index 8db46ac..0000000 --- a/contracts/deps/hk-cursor-v1.clar +++ /dev/null @@ -1,72 +0,0 @@ -(define-read-only (read-u8 (cursor { bytes: (buff 4096), pos: uint })) - (ok { - value: (buff-to-uint-be (unwrap! (as-max-len? (unwrap! (slice? (get bytes cursor) (get pos cursor) (+ (get pos cursor) u1)) (err u1)) u1) (err u1))), - next: { bytes: (get bytes cursor), pos: (+ (get pos cursor) u1) } - })) - -(define-read-only (read-u16 (cursor { bytes: (buff 4096), pos: uint })) - (ok { - value: (buff-to-uint-be (unwrap! (as-max-len? (unwrap! (slice? (get bytes cursor) (get pos cursor) (+ (get pos cursor) u2)) (err u1)) u2) (err u1))), - next: { bytes: (get bytes cursor), pos: (+ (get pos cursor) u2) } - })) - -(define-read-only (read-u32 (cursor { bytes: (buff 4096), pos: uint })) - (ok { - value: (buff-to-uint-be (unwrap! (as-max-len? (unwrap! (slice? (get bytes cursor) (get pos cursor) (+ (get pos cursor) u4)) (err u1)) u4) (err u1))), - next: { bytes: (get bytes cursor), pos: (+ (get pos cursor) u4) } - })) - -(define-read-only (read-u64 (cursor { bytes: (buff 4096), pos: uint })) - (ok { - value: (buff-to-uint-be (unwrap! (as-max-len? (unwrap! (slice? (get bytes cursor) (get pos cursor) (+ (get pos cursor) u8)) (err u1)) u8) (err u1))), - next: { bytes: (get bytes cursor), pos: (+ (get pos cursor) u8) } - })) - -(define-read-only (read-u128 (cursor { bytes: (buff 4096), pos: uint })) - (ok { - value: (buff-to-uint-be (unwrap! (as-max-len? (unwrap! (slice? (get bytes cursor) (get pos cursor) (+ (get pos cursor) u16)) (err u1)) u16) (err u1))), - next: { bytes: (get bytes cursor), pos: (+ (get pos cursor) u16) } - })) - -(define-read-only (read-buff-1 (cursor { bytes: (buff 4096), pos: uint })) - (ok { - value: (unwrap! (as-max-len? (unwrap! (slice? (get bytes cursor) (get pos cursor) (+ (get pos cursor) u1)) (err u1)) u1) (err u1)), - next: { bytes: (get bytes cursor), pos: (+ (get pos cursor) u1) } - })) - -(define-read-only (read-buff-4 (cursor { bytes: (buff 4096), pos: uint })) - (ok { - value: (unwrap! (as-max-len? (unwrap! (slice? (get bytes cursor) (get pos cursor) (+ (get pos cursor) u4)) (err u1)) u4) (err u1)), - next: { bytes: (get bytes cursor), pos: (+ (get pos cursor) u4) } - })) - -(define-read-only (read-buff-8 (cursor { bytes: (buff 4096), pos: uint })) - (ok { - value: (unwrap! (as-max-len? (unwrap! (slice? (get bytes cursor) (get pos cursor) (+ (get pos cursor) u8)) (err u1)) u8) (err u1)), - next: { bytes: (get bytes cursor), pos: (+ (get pos cursor) u8) } - })) - -(define-read-only (read-buff-20 (cursor { bytes: (buff 4096), pos: uint })) - (ok { - value: (unwrap! (as-max-len? (unwrap! (slice? (get bytes cursor) (get pos cursor) (+ (get pos cursor) u20)) (err u1)) u20) (err u1)), - next: { bytes: (get bytes cursor), pos: (+ (get pos cursor) u20) } - })) - -(define-read-only (read-buff-32 (cursor { bytes: (buff 4096), pos: uint })) - (ok { - value: (unwrap! (as-max-len? (unwrap! (slice? (get bytes cursor) (get pos cursor) (+ (get pos cursor) u32)) (err u1)) u32) (err u1)), - next: { bytes: (get bytes cursor), pos: (+ (get pos cursor) u32) } - })) - -(define-read-only (read-buff-65 (cursor { bytes: (buff 4096), pos: uint })) - (ok { - value: (unwrap! (as-max-len? (unwrap! (slice? (get bytes cursor) (get pos cursor) (+ (get pos cursor) u65)) (err u1)) u65) (err u1)), - next: { bytes: (get bytes cursor), pos: (+ (get pos cursor) u65) } - })) - - -(define-read-only (read-remaining-bytes-max-2048 (cursor { bytes: (buff 4096), pos: uint })) - (ok { - value: (unwrap! (as-max-len? (unwrap! (slice? (get bytes cursor) (get pos cursor) (+ (get pos cursor) (- (len (get bytes cursor)) (get pos cursor)))) (err u1)) u2048) (err u1)), - next: { bytes: (get bytes cursor), pos: (+ (get pos cursor) u32) } - })) diff --git a/contracts/deps/hk-cursor-v2.clar b/contracts/deps/hk-cursor-v2.clar deleted file mode 100644 index 13b823a..0000000 --- a/contracts/deps/hk-cursor-v2.clar +++ /dev/null @@ -1,118 +0,0 @@ -(define-read-only (read-u8 (cursor { bytes: (buff 8192), pos: uint })) - (ok { - value: (buff-to-uint-be (unwrap! (as-max-len? (unwrap! (slice? (get bytes cursor) (get pos cursor) (+ (get pos cursor) u1)) (err u1)) u1) (err u1))), - next: { bytes: (get bytes cursor), pos: (+ (get pos cursor) u1) } - })) - -(define-read-only (read-u16 (cursor { bytes: (buff 8192), pos: uint })) - (ok { - value: (buff-to-uint-be (unwrap! (as-max-len? (unwrap! (slice? (get bytes cursor) (get pos cursor) (+ (get pos cursor) u2)) (err u1)) u2) (err u1))), - next: { bytes: (get bytes cursor), pos: (+ (get pos cursor) u2) } - })) - -(define-read-only (read-u32 (cursor { bytes: (buff 8192), pos: uint })) - (ok { - value: (buff-to-uint-be (unwrap! (as-max-len? (unwrap! (slice? (get bytes cursor) (get pos cursor) (+ (get pos cursor) u4)) (err u1)) u4) (err u1))), - next: { bytes: (get bytes cursor), pos: (+ (get pos cursor) u4) } - })) - -(define-read-only (read-u64 (cursor { bytes: (buff 8192), pos: uint })) - (ok { - value: (buff-to-uint-be (unwrap! (as-max-len? (unwrap! (slice? (get bytes cursor) (get pos cursor) (+ (get pos cursor) u8)) (err u1)) u8) (err u1))), - next: { bytes: (get bytes cursor), pos: (+ (get pos cursor) u8) } - })) - -(define-read-only (read-u128 (cursor { bytes: (buff 8192), pos: uint })) - (ok { - value: (buff-to-uint-be (unwrap! (as-max-len? (unwrap! (slice? (get bytes cursor) (get pos cursor) (+ (get pos cursor) u16)) (err u1)) u16) (err u1))), - next: { bytes: (get bytes cursor), pos: (+ (get pos cursor) u16) } - })) - -(define-read-only (read-buff-1 (cursor { bytes: (buff 8192), pos: uint })) - (ok { - value: (unwrap! (as-max-len? (unwrap! (slice? (get bytes cursor) (get pos cursor) (+ (get pos cursor) u1)) (err u1)) u1) (err u1)), - next: { bytes: (get bytes cursor), pos: (+ (get pos cursor) u1) } - })) - -(define-read-only (read-buff-4 (cursor { bytes: (buff 8192), pos: uint })) - (ok { - value: (unwrap! (as-max-len? (unwrap! (slice? (get bytes cursor) (get pos cursor) (+ (get pos cursor) u4)) (err u1)) u4) (err u1)), - next: { bytes: (get bytes cursor), pos: (+ (get pos cursor) u4) } - })) - -(define-read-only (read-buff-8 (cursor { bytes: (buff 8192), pos: uint })) - (ok { - value: (unwrap! (as-max-len? (unwrap! (slice? (get bytes cursor) (get pos cursor) (+ (get pos cursor) u8)) (err u1)) u8) (err u1)), - next: { bytes: (get bytes cursor), pos: (+ (get pos cursor) u8) } - })) - -(define-read-only (read-buff-20 (cursor { bytes: (buff 8192), pos: uint })) - (ok { - value: (unwrap! (as-max-len? (unwrap! (slice? (get bytes cursor) (get pos cursor) (+ (get pos cursor) u20)) (err u1)) u20) (err u1)), - next: { bytes: (get bytes cursor), pos: (+ (get pos cursor) u20) } - })) - -(define-read-only (read-buff-32 (cursor { bytes: (buff 8192), pos: uint })) - (ok { - value: (unwrap! (as-max-len? (unwrap! (slice? (get bytes cursor) (get pos cursor) (+ (get pos cursor) u32)) (err u1)) u32) (err u1)), - next: { bytes: (get bytes cursor), pos: (+ (get pos cursor) u32) } - })) - -(define-read-only (read-buff-65 (cursor { bytes: (buff 8192), pos: uint })) - (ok { - value: (unwrap! (as-max-len? (unwrap! (slice? (get bytes cursor) (get pos cursor) (+ (get pos cursor) u65)) (err u1)) u65) (err u1)), - next: { bytes: (get bytes cursor), pos: (+ (get pos cursor) u65) } - })) - -(define-read-only (read-buff-max-len-1024 (cursor { bytes: (buff 8192), pos: uint }) (actual-len uint)) - (ok { - value: (unwrap! (as-max-len? (unwrap! (slice? (get bytes cursor) (get pos cursor) (+ (get pos cursor) actual-len)) (err u1)) u1024) (err u1)), - next: { bytes: (get bytes cursor), pos: (+ (get pos cursor) actual-len) } - })) - -(define-read-only (read-buff-max-len-2048 (cursor { bytes: (buff 8192), pos: uint }) (actual-len uint)) - (ok { - value: (unwrap! (as-max-len? (unwrap! (slice? (get bytes cursor) (get pos cursor) (+ (get pos cursor) actual-len)) (err u1)) u2048) (err u1)), - next: { bytes: (get bytes cursor), pos: (+ (get pos cursor) actual-len) } - })) - -(define-read-only (read-buff-max-len-8192 (cursor { bytes: (buff 8192), pos: uint }) (actual-len uint)) - (ok { - value: (unwrap! (as-max-len? (unwrap! (slice? (get bytes cursor) (get pos cursor) (+ (get pos cursor) actual-len)) (err u1)) u8192) (err u1)), - next: { bytes: (get bytes cursor), pos: (+ (get pos cursor) actual-len) } - })) - -(define-read-only (read-buff-max-len-255 (cursor { bytes: (buff 255), pos: uint }) (actual-len uint)) - (ok { - value: (unwrap! (as-max-len? (unwrap! (slice? (get bytes cursor) (get pos cursor) (+ (get pos cursor) actual-len)) (err u1)) u255) (err u1)), - next: { bytes: (get bytes cursor), pos: (+ (get pos cursor) actual-len) } - })) - -(define-read-only (read-buff-max-len-65535 (cursor { bytes: (buff 65535), pos: uint }) (actual-len uint)) - (ok { - value: (unwrap! (as-max-len? (unwrap! (slice? (get bytes cursor) (get pos cursor) (+ (get pos cursor) actual-len)) (err u1)) u65535) (err u1)), - next: { bytes: (get bytes cursor), pos: (+ (get pos cursor) actual-len) } - })) - -(define-read-only (read-remaining-bytes-max-2048 (cursor { bytes: (buff 8192), pos: uint })) - (ok { - value: (unwrap! (as-max-len? (unwrap! (slice? (get bytes cursor) (get pos cursor) (+ (get pos cursor) (- (len (get bytes cursor)) (get pos cursor)))) (err u1)) u2048) (err u1)), - next: { bytes: (get bytes cursor), pos: (+ (get pos cursor) u32) } - })) - -(define-read-only (new (bytes (buff 8192)) (offset (optional uint))) - { - value: none, - next: { bytes: bytes, pos: (match offset value value u0) } - }) - -(define-read-only (advance (cursor { bytes: (buff 8192), pos: uint }) (offset uint)) - { bytes: (get bytes cursor), pos: (+ (get pos cursor) offset) }) - -(define-read-only (slice (cursor { bytes: (buff 8192), pos: uint }) (size (optional uint))) - (unwrap-panic (slice? - (get bytes cursor) - (get pos cursor) - (match size - value (+ (get pos cursor) value) - (+ (get pos cursor) (- (len (get bytes cursor)) (get pos cursor))))))) diff --git a/contracts/deps/wormhole-core-trait.clar b/contracts/deps/wormhole-core-trait.clar deleted file mode 100644 index 51d2d79..0000000 --- a/contracts/deps/wormhole-core-trait.clar +++ /dev/null @@ -1,19 +0,0 @@ -(define-trait wormhole-core-trait - ( - ;; Parse and Verify cryptographic validity of a VAA - (parse-and-verify-vaa ((buff 2048)) (response { - version: uint, - guardian-set-id: uint, - signatures-len: uint , - signatures: (list 19 { guardian-id: uint, signature: (buff 65) }), - timestamp: uint, - nonce: uint, - emitter-chain: uint, - sequence: uint, - consistency-level: uint, - payload: (buff 2048), - guardians-public-keys: (list 19 { recovered-compressed-public-key: (response (buff 33) uint), guardian-id: uint }), - vaa-body-hash: (buff 32), - } uint)) - ) -) diff --git a/contracts/hiro-kit/hk-cursor-v1.clar b/contracts/hiro-kit/hk-cursor-v1.clar new file mode 100644 index 0000000..85eae10 --- /dev/null +++ b/contracts/hiro-kit/hk-cursor-v1.clar @@ -0,0 +1,133 @@ +;; Title: hiro-kit-cursor +;; Version: v1 + +(define-read-only (read-buff-1 (cursor { bytes: (buff 8192), pos: uint })) + (ok { + value: (unwrap! (as-max-len? (unwrap! (slice? (get bytes cursor) (get pos cursor) (+ (get pos cursor) u1)) (err u1)) u1) (err u1)), + next: { bytes: (get bytes cursor), pos: (+ (get pos cursor) u1) } + })) + +(define-read-only (read-buff-2 (cursor { bytes: (buff 8192), pos: uint })) + (ok { + value: (unwrap! (as-max-len? (unwrap! (slice? (get bytes cursor) (get pos cursor) (+ (get pos cursor) u2)) (err u1)) u2) (err u1)), + next: { bytes: (get bytes cursor), pos: (+ (get pos cursor) u2) } + })) + +(define-read-only (read-buff-4 (cursor { bytes: (buff 8192), pos: uint })) + (ok { + value: (unwrap! (as-max-len? (unwrap! (slice? (get bytes cursor) (get pos cursor) (+ (get pos cursor) u4)) (err u1)) u4) (err u1)), + next: { bytes: (get bytes cursor), pos: (+ (get pos cursor) u4) } + })) + +(define-read-only (read-buff-8 (cursor { bytes: (buff 8192), pos: uint })) + (ok { + value: (unwrap! (as-max-len? (unwrap! (slice? (get bytes cursor) (get pos cursor) (+ (get pos cursor) u8)) (err u1)) u8) (err u1)), + next: { bytes: (get bytes cursor), pos: (+ (get pos cursor) u8) } + })) + +(define-read-only (read-buff-16 (cursor { bytes: (buff 8192), pos: uint })) + (ok { + value: (unwrap! (as-max-len? (unwrap! (slice? (get bytes cursor) (get pos cursor) (+ (get pos cursor) u16)) (err u1)) u16) (err u1)), + next: { bytes: (get bytes cursor), pos: (+ (get pos cursor) u16) } + })) + +(define-read-only (read-buff-20 (cursor { bytes: (buff 8192), pos: uint })) + (ok { + value: (unwrap! (as-max-len? (unwrap! (slice? (get bytes cursor) (get pos cursor) (+ (get pos cursor) u20)) (err u1)) u20) (err u1)), + next: { bytes: (get bytes cursor), pos: (+ (get pos cursor) u20) } + })) + +(define-read-only (read-buff-32 (cursor { bytes: (buff 8192), pos: uint })) + (ok { + value: (unwrap! (as-max-len? (unwrap! (slice? (get bytes cursor) (get pos cursor) (+ (get pos cursor) u32)) (err u1)) u32) (err u1)), + next: { bytes: (get bytes cursor), pos: (+ (get pos cursor) u32) } + })) + +(define-read-only (read-buff-64 (cursor { bytes: (buff 8192), pos: uint })) + (ok { + value: (unwrap! (as-max-len? (unwrap! (slice? (get bytes cursor) (get pos cursor) (+ (get pos cursor) u64)) (err u1)) u64) (err u1)), + next: { bytes: (get bytes cursor), pos: (+ (get pos cursor) u64) } + })) + +(define-read-only (read-buff-65 (cursor { bytes: (buff 8192), pos: uint })) + (ok { + value: (unwrap! (as-max-len? (unwrap! (slice? (get bytes cursor) (get pos cursor) (+ (get pos cursor) u65)) (err u1)) u65) (err u1)), + next: { bytes: (get bytes cursor), pos: (+ (get pos cursor) u65) } + })) + +(define-read-only (read-buff-8192-max (cursor { bytes: (buff 8192), pos: uint }) (size (optional uint))) + (let ((min (get pos cursor)) + (max (match size value + (+ value (get pos cursor)) + (len (get bytes cursor))))) + (ok { + value: (unwrap! (as-max-len? (unwrap! (slice? (get bytes cursor) min max) (err u1)) u8192) (err u1)), + next: { bytes: (get bytes cursor), pos: max } + }))) + +(define-read-only (read-uint-8 (cursor { bytes: (buff 8192), pos: uint })) + (let ((cursor-bytes (try! (read-buff-1 cursor)))) + (ok (merge cursor-bytes { value: (buff-to-uint-be (get value cursor-bytes)) })))) + +(define-read-only (read-uint-16 (cursor { bytes: (buff 8192), pos: uint })) + (let ((cursor-bytes (try! (read-buff-2 cursor)))) + (ok (merge cursor-bytes { value: (buff-to-uint-be (get value cursor-bytes)) })))) + +(define-read-only (read-uint-32 (cursor { bytes: (buff 8192), pos: uint })) + (let ((cursor-bytes (try! (read-buff-4 cursor)))) + (ok (merge cursor-bytes { value: (buff-to-uint-be (get value cursor-bytes)) })))) + +(define-read-only (read-uint-64 (cursor { bytes: (buff 8192), pos: uint })) + (let ((cursor-bytes (try! (read-buff-8 cursor)))) + (ok (merge cursor-bytes { value: (buff-to-uint-be (get value cursor-bytes)) })))) + +(define-read-only (read-uint-128 (cursor { bytes: (buff 8192), pos: uint })) + (let ((cursor-bytes (try! (read-buff-16 cursor)))) + (ok (merge cursor-bytes { value: (buff-to-uint-be (get value cursor-bytes)) })))) + +(define-read-only (read-int-8 (cursor { bytes: (buff 8192), pos: uint })) + (let ((cursor-bytes (try! (read-buff-1 cursor)))) + (ok (merge + cursor-bytes + { value: (bit-shift-right (bit-shift-left (buff-to-int-be (get value cursor-bytes)) u120) u120) })))) + +(define-read-only (read-int-16 (cursor { bytes: (buff 8192), pos: uint })) + (let ((cursor-bytes (try! (read-buff-2 cursor)))) + (ok (merge + cursor-bytes + { value: (bit-shift-right (bit-shift-left (buff-to-int-be (get value cursor-bytes)) u112) u112) })))) + +(define-read-only (read-int-32 (cursor { bytes: (buff 8192), pos: uint })) + (let ((cursor-bytes (try! (read-buff-4 cursor)))) + (ok (merge + cursor-bytes + { value: (bit-shift-right (bit-shift-left (buff-to-int-be (get value cursor-bytes)) u96) u96) })))) + +(define-read-only (read-int-64 (cursor { bytes: (buff 8192), pos: uint })) + (let ((cursor-bytes (try! (read-buff-8 cursor)))) + (ok (merge + cursor-bytes + { value: (bit-shift-right (bit-shift-left (buff-to-int-be (get value cursor-bytes)) u64) u64) })))) + +(define-read-only (read-int-128 (cursor { bytes: (buff 8192), pos: uint })) + (let ((cursor-bytes (try! (read-buff-16 cursor)))) + (ok (merge + cursor-bytes + { value: (buff-to-int-be (get value cursor-bytes)) })))) + +(define-read-only (new (bytes (buff 8192)) (offset (optional uint))) + { + value: none, + next: { bytes: bytes, pos: (match offset value value u0) } + }) + +(define-read-only (advance (cursor { bytes: (buff 8192), pos: uint }) (offset uint)) + { bytes: (get bytes cursor), pos: (+ (get pos cursor) offset) }) + +(define-read-only (slice (cursor { bytes: (buff 8192), pos: uint }) (size (optional uint))) + (unwrap-panic (slice? + (get bytes cursor) + (get pos cursor) + (match size value + (+ (get pos cursor) value) + (len (get bytes cursor)))))) diff --git a/contracts/deps/hk-merkle-tree-keccak160-v1.clar b/contracts/hiro-kit/hk-merkle-tree-keccak160-v1.clar similarity index 95% rename from contracts/deps/hk-merkle-tree-keccak160-v1.clar rename to contracts/hiro-kit/hk-merkle-tree-keccak160-v1.clar index 78ae807..68bb25b 100644 --- a/contracts/deps/hk-merkle-tree-keccak160-v1.clar +++ b/contracts/hiro-kit/hk-merkle-tree-keccak160-v1.clar @@ -1,3 +1,5 @@ +;; Title: hiro-merkle-tree-keccak160 +;; Version: v1 (define-read-only (keccak160 (bytes (buff 1024))) (unwrap-panic (as-max-len? (unwrap-panic (slice? (keccak256 bytes) u0 u20)) u20))) diff --git a/contracts/pyth-governance-v1.clar b/contracts/pyth-governance-v1.clar new file mode 100644 index 0000000..a24f2d6 --- /dev/null +++ b/contracts/pyth-governance-v1.clar @@ -0,0 +1,366 @@ +;; Title: pyth-governance +;; Version: v1 +;; Check for latest version: https://github.com/hirosystems/stacks-pyth-bridge#latest-version +;; Report an issue: https://github.com/hirosystems/stacks-pyth-bridge/issues + +(use-trait pyth-proxy-trait .pyth-traits-v1.proxy-trait) +(use-trait pyth-decoder-trait .pyth-traits-v1.decoder-trait) +(use-trait pyth-storage-trait .pyth-traits-v1.storage-trait) +(use-trait wormhole-core-trait .wormhole-traits-v1.core-trait) + +(define-constant PTGM_MAGIC 0x5054474d) ;; 'PTGM': Pyth Governance Message + +;; VAA including some commands for administrating Pyth contract +;; The oracle contract address must be upgraded +(define-constant GOVERNANCE_UPGRADE_CONTRACT_PYTH_ORACLE 0x00) +;; Authorize governance change +(define-constant GOVERNANCE_AUTHORIZE_GOVERNANCE_DATA_SOURCE_TRANSFER 0x01) +;; Which wormhole emitter is allowed to send price updates +(define-constant GOVERNANCE_SET_DATA_SOURCES 0x02) +;; Fee is charged when you submit a new price +(define-constant GOVERNANCE_SET_FEE 0x03) +;; Emit a request for governance change +(define-constant GOVERNANCE_REQUEST_GOVERNANCE_DATA_SOURCE_TRANSFER 0x05) +;; Wormhole contract +(define-constant GOVERNANCE_UPGRADE_CONTRACT_WORMHOLE_CORE 0x06) +;; Fee is charged when you submit a new price +(define-constant GOVERNANCE_SET_RECIPIENT_ADDRESS 0xa0) + +(define-data-var fee-value + { mantissa: uint, exponent: uint } + { mantissa: u1, exponent: u1 }) +(define-data-var price-data-sources (buff 32) 0x) +(define-data-var governance-data-source + { emitter-chain: uint, emitter-address: (buff 32) } + { emitter-chain: u0, emitter-address: 0x }) +(define-data-var fee-recipient-address principal tx-sender) + +(define-map prices-data-sources uint + (list 255 { emitter-chain: uint, emitter-address: (buff 32) })) +(define-data-var current-prices-data-sources-id uint u0) + +(define-map execution-plans uint { + pyth-oracle-contract: principal, + pyth-decoder-contract: principal, + pyth-storage-contract: principal, + wormhole-core-contract: principal +}) +(define-data-var current-execution-plan-id uint u0) + +;; Execution plan management +;; Initialize governance v1 with v1 contracts +(begin + (map-insert execution-plans u0 { + pyth-oracle-contract: .pyth-oracle-v1, + pyth-decoder-contract: .pyth-pnau-decoder-v1, + pyth-storage-contract: .pyth-store-v1, + wormhole-core-contract: .wormhole-core-v1 + })) + +(define-read-only (check-execution-flow + (former-contract-caller principal) + (execution-plan-opt (optional { + pyth-storage-contract: , + pyth-decoder-contract: , + wormhole-core-contract: + }))) + (let ((expected-execution-plan (get-current-execution-plan)) + (success (if (is-eq contract-caller (get pyth-storage-contract expected-execution-plan)) + ;; The storage contract is checking its execution flow + ;; Must always be invoked by the proxy + (try! (expect-contract-call-performed-by-expected-oracle-contract former-contract-caller expected-execution-plan)) + ;; Other contract + (if (is-eq contract-caller (get pyth-decoder-contract expected-execution-plan)) + ;; The decoding contract is checking its execution flow + (let ((execution-plan (unwrap! execution-plan-opt (err u10)))) + ;; Must always be invoked by the proxy + (try! (expect-contract-call-performed-by-expected-oracle-contract former-contract-caller expected-execution-plan)) + ;; Ensure that wormhole contract is the one expected + (try! (expect-active-wormhole-contract (get wormhole-core-contract execution-plan) expected-execution-plan))) + (if (is-eq contract-caller (get pyth-oracle-contract expected-execution-plan)) + ;; The proxy contract is checking its execution flow + (let ((execution-plan (unwrap! execution-plan-opt (err u10)))) + ;; This contract must always be invoked by the proxy + (try! (expect-contract-call-performed-by-expected-oracle-contract former-contract-caller expected-execution-plan)) + ;; Ensure that storage contract is the one expected + (try! (expect-active-storage-contract (get pyth-storage-contract execution-plan) expected-execution-plan)) + ;; Ensure that decoder contract is the one expected + (try! (expect-active-decoder-contract (get pyth-decoder-contract execution-plan) expected-execution-plan)) + ;; Ensure that wormhole contract is the one expected + (try! (expect-active-wormhole-contract (get wormhole-core-contract execution-plan) expected-execution-plan))) + false))))) + (if success (ok true) (err u0)))) + +(define-read-only (check-storage-contract + (storage-contract )) + (let ((expected-execution-plan (get-current-execution-plan))) + ;; Ensure that storage contract is the one expected + (expect-active-storage-contract storage-contract expected-execution-plan))) + +(define-private (expect-contract-call-performed-by-expected-oracle-contract + (former-contract-caller principal) + (expected-plan { + pyth-oracle-contract: principal, + pyth-decoder-contract: principal, + pyth-storage-contract: principal, + wormhole-core-contract: principal + })) + (begin + (asserts! + (is-eq former-contract-caller (get pyth-oracle-contract expected-plan)) + (err u0)) + (ok true))) + +(define-private (expect-active-storage-contract + (storage-contract ) + (expected-plan { + pyth-oracle-contract: principal, + pyth-decoder-contract: principal, + pyth-storage-contract: principal, + wormhole-core-contract: principal + })) + (begin + (asserts! + (is-eq + (contract-of storage-contract) + (get pyth-storage-contract expected-plan)) (err u1)) + (ok true))) + +(define-private (expect-active-decoder-contract + (decoder-contract ) + (expected-plan { + pyth-oracle-contract: principal, + pyth-decoder-contract: principal, + pyth-storage-contract: principal, + wormhole-core-contract: principal + })) + (begin + (asserts! + (is-eq + (contract-of decoder-contract) + (get pyth-decoder-contract expected-plan)) (err u2)) + (ok true))) + +(define-private (expect-active-wormhole-contract + (wormhole-contract ) + (expected-plan { + pyth-oracle-contract: principal, + pyth-decoder-contract: principal, + pyth-storage-contract: principal, + wormhole-core-contract: principal + })) + (begin + (asserts! + (is-eq + (contract-of wormhole-contract) + (get wormhole-core-contract expected-plan)) (err u3)) + (ok true))) + +(define-read-only (get-current-execution-plan) + (unwrap-panic (map-get? execution-plans (var-get current-execution-plan-id)))) + +(define-read-only (get-fee-info) + (merge (var-get fee-value) { address: (var-get fee-recipient-address) })) + +(define-public (update-fee-value (vaa-bytes (buff 8192)) (wormhole-core-contract )) + (let ((expected-execution-plan (get-current-execution-plan)) + (vaa (try! (contract-call? wormhole-core-contract parse-and-verify-vaa vaa-bytes))) + (fee-value-update (try! (parse-and-verify-fee-value (get payload vaa))))) + ;; Ensure that the lastest wormhole contract is used + (try! (expect-active-wormhole-contract wormhole-core-contract expected-execution-plan)) + ;; + (var-set fee-value fee-value-update) + (ok fee-value-update))) + +(define-public (update-fee-recipient-addess (vaa-bytes (buff 8192)) (wormhole-core-contract )) + (let ((expected-execution-plan (get-current-execution-plan)) + (vaa (try! (contract-call? wormhole-core-contract parse-and-verify-vaa vaa-bytes))) + (fee-recipient-address-update (try! (parse-and-verify-fee-recipient-address (get payload vaa))))) + ;; Ensure that the lastest wormhole contract is used + (try! (expect-active-wormhole-contract wormhole-core-contract expected-execution-plan)) + ;; + (var-set fee-recipient-address fee-recipient-address-update) + (ok fee-recipient-address-update))) + +(define-public (update-wormhole-core-contract (vaa-bytes (buff 8192)) (wormhole-core-contract )) + (let ((expected-execution-plan (get-current-execution-plan)) + (next-execution-plan-id (+ (var-get current-execution-plan-id) u1)) + (vaa (try! (contract-call? wormhole-core-contract parse-and-verify-vaa vaa-bytes))) + (wormhole-core-address (try! (parse-and-verify-wormhole-core-contract (get payload vaa))))) + ;; Ensure that the lastest wormhole contract is used + (try! (expect-active-wormhole-contract wormhole-core-contract expected-execution-plan)) + ;; + (map-set execution-plans next-execution-plan-id (merge expected-execution-plan { wormhole-core-contract: wormhole-core-address })) + (ok wormhole-core-address))) + +(define-public (update-pyth-oracle-contract (vaa-bytes (buff 8192)) (wormhole-core-contract )) + (let ((expected-execution-plan (get-current-execution-plan)) + (next-execution-plan-id (+ (var-get current-execution-plan-id) u1)) + (vaa (try! (contract-call? wormhole-core-contract parse-and-verify-vaa vaa-bytes))) + (pyth-oracle-address (try! (parse-and-verify-pyth-oracle-contract (get payload vaa))))) + ;; Ensure that the lastest wormhole contract is used + (try! (expect-active-wormhole-contract wormhole-core-contract expected-execution-plan)) + ;; + (map-set execution-plans next-execution-plan-id (merge expected-execution-plan { pyth-oracle-contract: pyth-oracle-address })) + (ok pyth-oracle-address))) + +(define-public (update-prices-data-sources (vaa-bytes (buff 8192)) (wormhole-core-contract )) + (let ((expected-execution-plan (get-current-execution-plan)) + (next-prices-data-sources-id (+ (var-get current-prices-data-sources-id) u1)) + (vaa (try! (contract-call? wormhole-core-contract parse-and-verify-vaa vaa-bytes))) + (prices-data-sources-update (try! (parse-and-verify-prices-data-sources (get payload vaa))))) + ;; Ensure that the lastest wormhole contract is used + (try! (expect-active-wormhole-contract wormhole-core-contract expected-execution-plan)) + ;; + (map-set prices-data-sources next-prices-data-sources-id prices-data-sources-update) + (ok prices-data-sources-update))) + + +(define-private (parse-and-verify-governance-instruction (ptgm-bytes (buff 8192))) + (let ((cursor-magic (unwrap! (contract-call? .hk-cursor-v1 read-buff-4 { bytes: ptgm-bytes, pos: u0 }) + (err u0))) + (cursor-module (unwrap! (contract-call? .hk-cursor-v1 read-uint-8 (get next cursor-magic)) + (err u1))) + (cursor-action (unwrap! (contract-call? .hk-cursor-v1 read-buff-1 (get next cursor-module)) + (err u2))) + (cursor-target-chain-id (unwrap! (contract-call? .hk-cursor-v1 read-uint-16 (get next cursor-action)) + (err u3)))) + ;; Check magic bytes + (asserts! (is-eq (get value cursor-magic) PTGM_MAGIC) (err u1)) + ;; + (ok { + action: (get value cursor-action), + target-chain-id: (get value cursor-target-chain-id), + module: (get value cursor-module), + cursor: cursor-target-chain-id + }))) + +(define-private (parse-and-verify-fee-value (fee-value-bytes (buff 8192))) + (let ((gi (try! (parse-and-verify-governance-instruction fee-value-bytes)))) + (asserts! (is-eq (get action gi) GOVERNANCE_SET_FEE) (err u1)) + ;; TODO: Check emitter-chain and emitter-address + (let ((cursor-mantissa (unwrap! (contract-call? .hk-cursor-v1 read-uint-64 (get next (get cursor gi))) + (err u100))) + (cursor-exponent (unwrap! (contract-call? .hk-cursor-v1 read-uint-64 (get next cursor-mantissa)) + (err u100)))) + (ok { + mantissa: (get value cursor-mantissa), + exponent: (get value cursor-exponent) + })))) + +(define-private (parse-and-verify-fee-recipient-address (fee-recipient-bytes (buff 8192))) + (let ((gi (try! (parse-and-verify-governance-instruction fee-recipient-bytes)))) + (asserts! (is-eq (get action gi) GOVERNANCE_SET_RECIPIENT_ADDRESS) (err u1)) + ;; TODO: Check emitter-chain and emitter-address + (let ((cursor-version (unwrap! (contract-call? .hk-cursor-v1 read-buff-1 (get next (get cursor gi))) + (err u100))) + (cursor-hash-bytes (unwrap! (contract-call? .hk-cursor-v1 read-buff-20 (get next cursor-version)) + (err u100))) + (cursor-contract-name (unwrap! (contract-call? .hk-cursor-v1 read-buff-8192-max (get next cursor-hash-bytes) none) + (err u100))) + (recipient (unwrap! + (if (is-eq u0 (len (get value cursor-contract-name))) + (principal-construct? + (get value cursor-version) + (get value cursor-hash-bytes)) + (principal-construct? + (get value cursor-version) + (get value cursor-hash-bytes) + (unwrap! (from-consensus-buff? (string-ascii 40) (get value cursor-contract-name)) (err u100)))) + (err u100)))) + (ok recipient)))) + +(define-private (parse-and-verify-wormhole-core-contract (wormhole-core-contract-bytes (buff 8192))) + (let ((gi (try! (parse-and-verify-governance-instruction wormhole-core-contract-bytes)))) + (asserts! (is-eq (get action gi) GOVERNANCE_UPGRADE_CONTRACT_WORMHOLE_CORE) (err u1)) + ;; TODO: Check emitter-chain and emitter-address + (let ((cursor-version (unwrap! (contract-call? .hk-cursor-v1 read-buff-1 (get next (get cursor gi))) + (err u100))) + (cursor-hash-bytes (unwrap! (contract-call? .hk-cursor-v1 read-buff-20 (get next cursor-version)) + (err u100))) + (cursor-contract-name (unwrap! (contract-call? .hk-cursor-v1 read-buff-8192-max (get next cursor-hash-bytes) none) + (err u100))) + (wormhole-core-address (unwrap! + (principal-construct? + (get value cursor-version) + (get value cursor-hash-bytes) + (unwrap! (from-consensus-buff? (string-ascii 40) (get value cursor-contract-name)) (err u100))) + (err u100)))) + (ok wormhole-core-address)))) + +(define-private (parse-and-verify-pyth-oracle-contract (pyth-oracle-contract-bytes (buff 8192))) + (let ((gi (try! (parse-and-verify-governance-instruction pyth-oracle-contract-bytes)))) + (asserts! (is-eq (get action gi) GOVERNANCE_UPGRADE_CONTRACT_PYTH_ORACLE) (err u1)) + ;; TODO: Check emitter-chain and emitter-address + (let ((cursor-version (unwrap! (contract-call? .hk-cursor-v1 read-buff-1 (get next (get cursor gi))) + (err u100))) + (cursor-hash-bytes (unwrap! (contract-call? .hk-cursor-v1 read-buff-20 (get next cursor-version)) + (err u100))) + (cursor-contract-name (unwrap! (contract-call? .hk-cursor-v1 read-buff-8192-max (get next cursor-hash-bytes) none) + (err u100))) + (pyth-oracle-address (unwrap! + (principal-construct? + (get value cursor-version) + (get value cursor-hash-bytes) + (unwrap! (from-consensus-buff? (string-ascii 40) (get value cursor-contract-name)) (err u100))) + (err u100)))) + (ok pyth-oracle-address)))) + +(define-private (parse-and-verify-prices-data-sources (prices-data-sources-bytes (buff 8192))) + (let ((gi (try! (parse-and-verify-governance-instruction prices-data-sources-bytes)))) + (asserts! (is-eq (get action gi) GOVERNANCE_SET_DATA_SOURCES) (err u1)) + ;; TODO: Check emitter-chain and emitter-address + (let ((cursor-num-data-sources (try! (contract-call? .hk-cursor-v1 read-uint-8 { bytes: prices-data-sources-bytes, pos: u0 }))) + (cursor-data-sources-bytes (contract-call? .hk-cursor-v1 slice (get next cursor-num-data-sources) none)) + (data-sources (get result (fold parse-data-sources cursor-data-sources-bytes { + result: (list), + cursor: { + index: u0, + next-update-index: u0 + }, + bytes: cursor-data-sources-bytes, + limit: (get value cursor-num-data-sources) + })))) + (ok data-sources)))) + +(define-private (parse-data-sources + (entry (buff 1)) + (acc { + cursor: { + index: uint, + next-update-index: uint + }, + bytes: (buff 8192), + result: (list 255 { emitter-chain: uint, emitter-address: (buff 32) }), + limit: uint + })) + (if (is-eq (len (get result acc)) (get limit acc)) + acc + (if (is-eq (get index (get cursor acc)) (get next-update-index (get cursor acc))) + ;; Parse update + (let ((buffer (contract-call? .hk-cursor-v1 new (get bytes acc) (some (get index (get cursor acc))))) + (cursor-emitter-chain (unwrap-panic (contract-call? .hk-cursor-v1 read-uint-8 (get next buffer)))) + (cursor-emitter-address (unwrap-panic (contract-call? .hk-cursor-v1 read-buff-32 (get next cursor-emitter-chain))))) + ;; Perform assertions + { + cursor: { + index: (+ (get index (get cursor acc)) u1), + next-update-index: (+ (get index (get cursor acc)) u33), + }, + bytes: (get bytes acc), + result: (unwrap-panic (as-max-len? (append (get result acc) { + emitter-chain: (get value cursor-emitter-chain), + emitter-address: (get value cursor-emitter-address) + }) u255)), + limit: (get limit acc), + }) + ;; Increment position + { + cursor: { + index: (+ (get index (get cursor acc)) u1), + next-update-index: (get next-update-index (get cursor acc)), + }, + bytes: (get bytes acc), + result: (get result acc), + limit: (get limit acc) + }))) diff --git a/contracts/pyth-oracle-trait.clar b/contracts/pyth-oracle-trait.clar deleted file mode 100644 index 05d6ea2..0000000 --- a/contracts/pyth-oracle-trait.clar +++ /dev/null @@ -1,18 +0,0 @@ -(define-trait pyth-oracle-trait - ( - (update-prices-feeds ((list 4 (buff 2048))) (response (list 4 { - price-feed-id: (buff 32), - price: (buff 8), - conf: (buff 8), - expo: (buff 4), - ema-price: (buff 8), - ema-conf: (buff 8), - status: (buff 1), - attestation-time: (buff 8), - publish-time: (buff 8), - prev-publish-time: (buff 8), - prev-price: (buff 8), - prev-conf: (buff 8), - }) uint)) - ) -) diff --git a/contracts/pyth-oracle-v1.clar b/contracts/pyth-oracle-v1.clar index c504570..313f942 100644 --- a/contracts/pyth-oracle-v1.clar +++ b/contracts/pyth-oracle-v1.clar @@ -3,302 +3,43 @@ ;; Check for latest version: https://github.com/hirosystems/stacks-pyth-bridge#latest-version ;; Report an issue: https://github.com/hirosystems/stacks-pyth-bridge/issues -;;;; Traits - -;;;; Constants - -;; Price Feeds Ids (https://pyth.network/developers/price-feed-ids#pyth-evm-mainnet) -(define-constant STX_USD 0xec7a775f46379b5e943c3526b1c8d54cd49749176b0b98e02dde68d1bd335c17) -(define-constant BTC_USD 0xe62df6c8b4a85fe1a67db44dc12de5db330f7ac66b72dc658afedf0f4a415b43) -(define-constant ETH_USD 0xff61491a931112ddf1bd8147cd1b641375f79f5825126d665480874634fd0ace) - -(define-constant PNAU_MAGIC 0x504e4155) ;; 'PNAU': Pyth Network Accumulator Update -(define-constant AUWV_MAGIC 0x41555756) ;; 'AUWV': Accumulator Update Wormhole Verficiation -(define-constant PYTHNET_MAJOR_VERSION u1) -(define-constant PYTHNET_MINOR_VERSION u0) +(use-trait pyth-storage-trait .pyth-traits-v1.storage-trait) +(use-trait pyth-decoder-trait .pyth-traits-v1.decoder-trait) +(use-trait wormhole-core-trait .wormhole-traits-v1.core-trait) ;; Generic error (define-constant ERR_PANIC (err u0)) -;; Unable to price feed magic bytes -(define-constant ERR_MAGIC_BYTES (err u2001)) -;; Unable to parse major version -(define-constant ERR_VERSION_MAJ (err u2002)) -;; Unable to parse minor version -(define-constant ERR_VERSION_MIN (err u2003)) -;; Unable to parse trailing header size -(define-constant ERR_HEADER_TRAILING_SIZE (err u2004)) -;; Unable to parse update type -(define-constant ERR_PROOF_TYPE (err u2005)) -;; Merkle root mismatch -(define-constant MERKLE_ROOT_MISMATCH (err u2006)) -;; Price not found -(define-constant ERR_NOT_FOUND (err u0)) - -;;;; Data maps -;; -(define-map prices - { price-feed-id: (buff 32) } - { - price: int, - conf: uint, - expo: int, - attestation-time: uint, - ema-price: int, - ema-conf: uint, - status: uint, - publish-time: uint, - prev-publish-time: uint, - prev-price: int, - prev-conf: uint, - }) - -(define-data-var watched-price-feeds - (list 1024 (buff 32)) - (list STX_USD BTC_USD ETH_USD)) - -;;;; Public functions -;; -(define-public (update-prices-feeds (pnau-bytes (buff 8192))) - (let ((cursor-pnau-header (try! (parse-pnau-header pnau-bytes))) - (cursor-pnau-vaa-size (try! (contract-call? .hk-cursor-v2 read-u16 (get next cursor-pnau-header)))) - (cursor-pnau-vaa (try! (contract-call? .hk-cursor-v2 read-buff-max-len-2048 (get next cursor-pnau-vaa-size) (get value cursor-pnau-vaa-size)))) - (vaa (try! (contract-call? .wormhole-core-dev-preview-1 parse-and-verify-vaa (get value cursor-pnau-vaa)))) - (cursor-merkle-root-data (try! (parse-merkle-root-data-from-vaa-payload (get payload vaa)))) - (decoded-prices-updates (parse-and-verify-prices-updates - (contract-call? .hk-cursor-v2 slice (get next cursor-pnau-vaa) none) - (get merkle-root-hash (get value cursor-merkle-root-data))))) - ;; (watched-prices-feeds (var-get watched-price-feeds)) - ;; (updated-prices-feeds (get updated-prices-feeds (fold process-prices-attestations-batch decoded-prices-attestations-batches { input: watched-prices-feeds, updated-prices-feeds: (list) })))) - (ok { root-hash: (get value cursor-merkle-root-data), updates: decoded-prices-updates }))) - -;;;; Read only functions -;; - -(define-read-only (read-price-feed (price-feed-id (buff 32))) - (let ((price-feed-entry (unwrap! (map-get? prices { price-feed-id: price-feed-id }) ERR_NOT_FOUND))) - (ok price-feed-entry))) - -;;;; Private functions -;; -(define-private (parse-merkle-root-data-from-vaa-payload (payload-vaa-bytes (buff 2048))) - (let ((cursor-payload-type (unwrap! (contract-call? .hk-cursor-v2 read-buff-4 { bytes: payload-vaa-bytes, pos: u0 }) - (err u0))) - (cursor-wh-update-type (unwrap! (contract-call? .hk-cursor-v2 read-u8 (get next cursor-payload-type)) - (err u0))) - (cursor-merkle-root-slot (unwrap! (contract-call? .hk-cursor-v2 read-u64 (get next cursor-wh-update-type)) - (err u0))) - (cursor-merkle-root-ring-size (unwrap! (contract-call? .hk-cursor-v2 read-u32 (get next cursor-merkle-root-slot)) - (err u0))) - (cursor-merkle-root-hash (unwrap! (contract-call? .hk-cursor-v2 read-buff-20 (get next cursor-merkle-root-ring-size)) - (err u0)))) - ;; Check payload type - (asserts! (is-eq (get value cursor-payload-type) AUWV_MAGIC) ERR_MAGIC_BYTES) - ;; Check update type - (asserts! (is-eq (get value cursor-wh-update-type) u0) (err u999)) - (ok { - value: { - merkle-root-slot: (get value cursor-merkle-root-slot), - merkle-root-ring-size: (get value cursor-merkle-root-ring-size), - merkle-root-hash: (get value cursor-merkle-root-hash), - payload-type: (get value cursor-payload-type) - }, - next: (get next cursor-merkle-root-hash) - }))) - -(define-read-only (parse-pnau-header (pf-bytes (buff 8192))) - (let ((cursor-magic (unwrap! (contract-call? .hk-cursor-v2 read-buff-4 { bytes: pf-bytes, pos: u0 }) - ERR_MAGIC_BYTES)) - (cursor-version-maj (unwrap! (contract-call? .hk-cursor-v2 read-u8 (get next cursor-magic)) - ERR_VERSION_MAJ)) - (cursor-version-min (unwrap! (contract-call? .hk-cursor-v2 read-u8 (get next cursor-version-maj)) - ERR_VERSION_MIN)) - (cursor-header-trailing-size (unwrap! (contract-call? .hk-cursor-v2 read-u8 (get next cursor-version-min)) - ERR_HEADER_TRAILING_SIZE)) - (cursor-proof-type (unwrap! (contract-call? .hk-cursor-v2 read-u8 { - bytes: pf-bytes, - pos: (+ (get pos (get next cursor-header-trailing-size)) (get value cursor-header-trailing-size))}) - ERR_PROOF_TYPE))) - ;; Check magic bytes - (asserts! (is-eq (get value cursor-magic) PNAU_MAGIC) ERR_MAGIC_BYTES) - ;; Check major version - (asserts! (is-eq (get value cursor-version-maj) PYTHNET_MAJOR_VERSION) ERR_VERSION_MAJ) - ;; Check minor version - (asserts! (is-eq (get value cursor-version-min) PYTHNET_MINOR_VERSION) ERR_VERSION_MIN) - ;; Check proof type - (asserts! (is-eq (get value cursor-proof-type) u0) ERR_PROOF_TYPE) - (ok { - value: { - magic: (get value cursor-magic), - version-maj: (get value cursor-version-maj), - version-min: (get value cursor-version-min), - header-trailing-size: (get value cursor-header-trailing-size), - proof-type: (get value cursor-proof-type) - }, - next: (get next cursor-proof-type) - }))) - -(define-read-only (parse-and-verify-prices-updates (bytes (buff 8192)) (merkle-root-hash (buff 20))) - (let ((cursor-num-updates (try! (contract-call? .hk-cursor-v2 read-u8 { bytes: bytes, pos: u0 }))) - (cursor-updates-bytes (contract-call? .hk-cursor-v2 slice (get next cursor-num-updates) none)) - (updates (get result (fold parse-price-info-and-proof cursor-updates-bytes { - result: (list), - cursor: { - index: u0, - next-update-index: u0 - }, - bytes: cursor-updates-bytes, - limit: (get value cursor-num-updates) - }))) - (merkle-proof-checks-success (get result (fold check-merkle-proof updates { - result: true, - merkle-root-hash: merkle-root-hash - })))) - (asserts! merkle-proof-checks-success MERKLE_ROOT_MISMATCH) - (ok updates))) - -(define-private (check-merkle-proof - (entry - { - price-identifier: (buff 32), - price: int, - conf: uint, - expo: int, - publish-time: uint, - prev-publish-time: uint, - ema-price: int, - ema-conf: uint, - proof: (list 128 (buff 20)), - leaf-bytes: (buff 255) - }) - (acc - { - merkle-root-hash: (buff 20), - result: bool, - })) - { - merkle-root-hash: (get merkle-root-hash acc), - result: (and (get result acc) - (contract-call? .hk-merkle-tree-keccak160-v1 check-proof - (get merkle-root-hash acc) - (get leaf-bytes entry) - (get proof entry))) - }) - -(define-private (parse-price-info-and-proof - (entry (buff 1)) - (acc { - cursor: { - index: uint, - next-update-index: uint - }, - bytes: (buff 8192), - result: (list 64 { - price-identifier: (buff 32), - price: int, - conf: uint, - expo: int, - publish-time: uint, - prev-publish-time: uint, - ema-price: int, - ema-conf: uint, - proof: (list 128 (buff 20)), - leaf-bytes: (buff 255) - }), - limit: uint - })) - (if (is-eq (len (get result acc)) (get limit acc)) - acc - (if (is-eq (get index (get cursor acc)) (get next-update-index (get cursor acc))) - ;; Parse update - (let ((cursor-update (contract-call? .hk-cursor-v2 new (get bytes acc) (some (get index (get cursor acc))))) - (cursor-message-size (unwrap-panic (contract-call? .hk-cursor-v2 read-u16 (get next cursor-update)))) - (cursor-message-type (unwrap-panic (contract-call? .hk-cursor-v2 read-u8 (get next cursor-message-size)))) - (cursor-price-identifier (unwrap-panic (contract-call? .hk-cursor-v2 read-buff-32 (get next cursor-message-type)))) - (cursor-price (unwrap-panic (contract-call? .hk-cursor-v2 read-buff-8 (get next cursor-price-identifier)))) - (cursor-conf (unwrap-panic (contract-call? .hk-cursor-v2 read-buff-8 (get next cursor-price)))) - (cursor-expo (unwrap-panic (contract-call? .hk-cursor-v2 read-buff-4 (get next cursor-conf)))) - (cursor-publish-time (unwrap-panic (contract-call? .hk-cursor-v2 read-buff-8 (get next cursor-expo)))) - (cursor-prev-publish-time (unwrap-panic (contract-call? .hk-cursor-v2 read-buff-8 (get next cursor-publish-time)))) - (cursor-ema-price (unwrap-panic (contract-call? .hk-cursor-v2 read-buff-8 (get next cursor-prev-publish-time)))) - (cursor-ema-conf (unwrap-panic (contract-call? .hk-cursor-v2 read-buff-8 (get next cursor-ema-price)))) - (cursor-proof (contract-call? .hk-cursor-v2 advance (get next cursor-message-size) (get value cursor-message-size))) - (cursor-proof-size (unwrap-panic (contract-call? .hk-cursor-v2 read-u8 cursor-proof))) - (proof-bytes (contract-call? .hk-cursor-v2 slice (get next cursor-proof-size) none)) - (leaf-bytes (contract-call? .hk-cursor-v2 slice (get next cursor-message-size) (some (get value cursor-message-size)))) - (proof (get result (fold parse-proof proof-bytes { - result: (list), - cursor: { - index: u0, - next-update-index: u0 - }, - bytes: proof-bytes, - limit: (get value cursor-proof-size) - })))) - { - cursor: { - index: (+ (get index (get cursor acc)) u1), - next-update-index: (+ (get index (get cursor acc)) (+ (get pos (get next cursor-proof-size)) (get value cursor-proof-size))), - }, - bytes: (get bytes acc), - result: (unwrap-panic (as-max-len? (append (get result acc) { - price-identifier: (get value cursor-price-identifier), - price: (buff-to-int-be (get value cursor-price)), - conf: (buff-to-uint-be (get value cursor-conf)), - expo: (buff-to-int-be (get value cursor-expo)), - publish-time: (buff-to-uint-be (get value cursor-publish-time)), - prev-publish-time: (buff-to-uint-be (get value cursor-prev-publish-time)), - ema-price: (buff-to-int-be (get value cursor-ema-price)), - ema-conf: (buff-to-uint-be (get value cursor-ema-conf)), - proof: proof, - leaf-bytes: (unwrap-panic (as-max-len? leaf-bytes u255)) - }) u64)), - limit: (get limit acc), - }) - ;; Increment position - { - cursor: { - index: (+ (get index (get cursor acc)) u1), - next-update-index: (get next-update-index (get cursor acc)), - }, - bytes: (get bytes acc), - result: (get result acc), - limit: (get limit acc), - }))) - -(define-private (parse-proof - (entry (buff 1)) - (acc { - cursor: { - index: uint, - next-update-index: uint - }, - bytes: (buff 8192), - result: (list 128 (buff 20)), - limit: uint - })) - (if (is-eq (len (get result acc)) (get limit acc)) - acc - (if (is-eq (get index (get cursor acc)) (get next-update-index (get cursor acc))) - ;; Parse update - (let ((cursor-hash (contract-call? .hk-cursor-v2 new (get bytes acc) (some (get index (get cursor acc))))) - (hash (get value (unwrap-panic (contract-call? .hk-cursor-v2 read-buff-20 (get next cursor-hash)))))) - ;; Perform assertions - { - cursor: { - index: (+ (get index (get cursor acc)) u1), - next-update-index: (+ (get index (get cursor acc)) u20), - }, - bytes: (get bytes acc), - result: (unwrap-panic (as-max-len? (append (get result acc) hash) u128)), - limit: (get limit acc), - }) - ;; Increment position - { - cursor: { - index: (+ (get index (get cursor acc)) u1), - next-update-index: (get next-update-index (get cursor acc)), - }, - bytes: (get bytes acc), - result: (get result acc), - limit: (get limit acc) - }))) +;; Balance insufficient for handling fee +(define-constant ERR_BALANCE_INSUFFICIENT (err u4002)) + +(define-public (read-price-feed + (price-feed-id (buff 32)) + (pyth-storage-address )) + (begin + ;; Check execution flow + (try! (contract-call? .pyth-governance-v1 check-storage-contract pyth-storage-address)) + ;; Perform contract-call + (contract-call? pyth-storage-address read price-feed-id))) + +(define-public (verify-and-update-price-feeds + (price-feed-bytes (buff 8192)) + (execution-plan { + pyth-storage-contract: , + pyth-decoder-contract: , + wormhole-core-contract: + })) + (begin + ;; Check execution flow + (try! (contract-call? .pyth-governance-v1 check-execution-flow contract-caller (some execution-plan))) + ;; Perform contract-call + (let ((pyth-decoder-contract (get pyth-decoder-contract execution-plan)) + (wormhole-core-contract (get wormhole-core-contract execution-plan)) + (pyth-storage-contract (get pyth-storage-contract execution-plan)) + (prices-updates (try! (contract-call? pyth-decoder-contract decode-and-verify-price-feeds price-feed-bytes wormhole-core-contract))) + (fee-info (contract-call? .pyth-governance-v1 get-fee-info)) + (fee-amount (* (get mantissa fee-info) (pow u10 (get exponent fee-info))))) + ;; Charge fee + (unwrap! (stx-transfer? (* (len prices-updates) fee-amount) tx-sender (get address fee-info)) (err u0)) + ;; Update storage + (try! (contract-call? pyth-storage-contract write-batch prices-updates)) + (ok prices-updates)))) diff --git a/contracts/pyth-oracle-dev-preview-1.clar b/contracts/pyth-p2wh-decoder-v1.clar similarity index 99% rename from contracts/pyth-oracle-dev-preview-1.clar rename to contracts/pyth-p2wh-decoder-v1.clar index 73cac04..dc3ab3d 100644 --- a/contracts/pyth-oracle-dev-preview-1.clar +++ b/contracts/pyth-p2wh-decoder-v1.clar @@ -1,4 +1,4 @@ -;; Title: pyth-oracle +;; Title: pyth-p2wh-decoder-v1 ;; Version: Developer Preview 1 ;; Check for latest version: https://github.com/hirosystems/stacks-pyth-bridge#latest-version ;; Report an issue: https://github.com/hirosystems/stacks-pyth-bridge/issues @@ -365,4 +365,4 @@ (ok { price-feed-header: (get value cursor-price-feed-header), udpate-type: (get value cursor-udpate-type), - }))) + }))) \ No newline at end of file diff --git a/contracts/pyth-pnau-decoder-v1.clar b/contracts/pyth-pnau-decoder-v1.clar new file mode 100644 index 0000000..02c8e0b --- /dev/null +++ b/contracts/pyth-pnau-decoder-v1.clar @@ -0,0 +1,310 @@ +;; Title: pyth-pnau-decoder +;; Version: v1 +;; Check for latest version: https://github.com/hirosystems/stacks-pyth-bridge#latest-version +;; Report an issue: https://github.com/hirosystems/stacks-pyth-bridge/issues + +;;;; Traits +(impl-trait .pyth-traits-v1.decoder-trait) +(use-trait wormhole-core-trait .wormhole-traits-v1.core-trait) + +;;;; Constants + +;; Price Feeds Ids (https://pyth.network/developers/price-feed-ids#pyth-evm-mainnet) +(define-constant STX_USD 0xec7a775f46379b5e943c3526b1c8d54cd49749176b0b98e02dde68d1bd335c17) +(define-constant BTC_USD 0xe62df6c8b4a85fe1a67db44dc12de5db330f7ac66b72dc658afedf0f4a415b43) +(define-constant ETH_USD 0xff61491a931112ddf1bd8147cd1b641375f79f5825126d665480874634fd0ace) + +(define-constant PNAU_MAGIC 0x504e4155) ;; 'PNAU': Pyth Network Accumulator Update +(define-constant AUWV_MAGIC 0x41555756) ;; 'AUWV': Accumulator Update Wormhole Verficiation +(define-constant PYTHNET_MAJOR_VERSION u1) +(define-constant PYTHNET_MINOR_VERSION u0) + +;; Generic error +(define-constant ERR_PANIC (err u0)) +;; Unable to price feed magic bytes +(define-constant ERR_MAGIC_BYTES (err u2001)) +;; Unable to parse major version +(define-constant ERR_VERSION_MAJ (err u2002)) +;; Unable to parse minor version +(define-constant ERR_VERSION_MIN (err u2003)) +;; Unable to parse trailing header size +(define-constant ERR_HEADER_TRAILING_SIZE (err u2004)) +;; Unable to parse update type +(define-constant ERR_PROOF_TYPE (err u2005)) +;; Merkle root mismatch +(define-constant MERKLE_ROOT_MISMATCH (err u2006)) +;; Price not found +(define-constant ERR_NOT_FOUND (err u0)) + +;;;; Public functions +(define-public (decode-and-verify-price-feeds (pnau-bytes (buff 8192)) (wormhole-core-address )) + (begin + ;; Ensure that updates are always coming from the proxy contract + (asserts! (is-eq contract-caller .pyth-proxy-v1) (err u1)) + ;; Proceed to update + (let ((prices-updates (try! (decode-pnau-price-update pnau-bytes wormhole-core-address)))) + (ok prices-updates)))) + +;;;; Private functions +;; Note: wormhole-core-address is checked upstream so we will ignore the following warning +;; #[filter(pnau-bytes, wormhole-core-address)] +(define-public (decode-pnau-price-update (pnau-bytes (buff 8192)) (wormhole-core-address )) + (let ((cursor-pnau-header (try! (parse-pnau-header pnau-bytes))) + (cursor-pnau-vaa-size (try! (contract-call? .hk-cursor-v1 read-uint-16 (get next cursor-pnau-header)))) + (cursor-pnau-vaa (try! (contract-call? .hk-cursor-v1 read-buff-8192-max (get next cursor-pnau-vaa-size) (some (get value cursor-pnau-vaa-size))))) + (vaa (try! (contract-call? wormhole-core-address parse-and-verify-vaa (get value cursor-pnau-vaa)))) + (cursor-merkle-root-data (try! (parse-merkle-root-data-from-vaa-payload (get payload vaa)))) + (decoded-prices-updates (try! (parse-and-verify-prices-updates + (contract-call? .hk-cursor-v1 slice (get next cursor-pnau-vaa) none) + (get merkle-root-hash (get value cursor-merkle-root-data))))) + (prices-updates (map cast-decoded-price decoded-prices-updates))) + ;; (watched-prices-feeds (var-get watched-price-feeds)) + ;; (updated-prices-feeds (get updated-prices-feeds (fold process-prices-attestations-batch decoded-prices-attestations-batches { input: watched-prices-feeds, updated-prices-feeds: (list) })))) + (ok prices-updates))) + +(define-private (parse-merkle-root-data-from-vaa-payload (payload-vaa-bytes (buff 8192))) + (let ((cursor-payload-type (unwrap! (contract-call? .hk-cursor-v1 read-buff-4 { bytes: payload-vaa-bytes, pos: u0 }) + (err u0))) + (cursor-wh-update-type (unwrap! (contract-call? .hk-cursor-v1 read-uint-8 (get next cursor-payload-type)) + (err u0))) + (cursor-merkle-root-slot (unwrap! (contract-call? .hk-cursor-v1 read-uint-64 (get next cursor-wh-update-type)) + (err u0))) + (cursor-merkle-root-ring-size (unwrap! (contract-call? .hk-cursor-v1 read-uint-32 (get next cursor-merkle-root-slot)) + (err u0))) + (cursor-merkle-root-hash (unwrap! (contract-call? .hk-cursor-v1 read-buff-20 (get next cursor-merkle-root-ring-size)) + (err u0)))) + ;; Check payload type + (asserts! (is-eq (get value cursor-payload-type) AUWV_MAGIC) ERR_MAGIC_BYTES) + ;; Check update type + (asserts! (is-eq (get value cursor-wh-update-type) u0) (err u999)) + (ok { + value: { + merkle-root-slot: (get value cursor-merkle-root-slot), + merkle-root-ring-size: (get value cursor-merkle-root-ring-size), + merkle-root-hash: (get value cursor-merkle-root-hash), + payload-type: (get value cursor-payload-type) + }, + next: (get next cursor-merkle-root-hash) + }))) + +(define-read-only (parse-pnau-header (pf-bytes (buff 8192))) + (let ((cursor-magic (unwrap! (contract-call? .hk-cursor-v1 read-buff-4 { bytes: pf-bytes, pos: u0 }) + ERR_MAGIC_BYTES)) + (cursor-version-maj (unwrap! (contract-call? .hk-cursor-v1 read-uint-8 (get next cursor-magic)) + ERR_VERSION_MAJ)) + (cursor-version-min (unwrap! (contract-call? .hk-cursor-v1 read-uint-8 (get next cursor-version-maj)) + ERR_VERSION_MIN)) + (cursor-header-trailing-size (unwrap! (contract-call? .hk-cursor-v1 read-uint-8 (get next cursor-version-min)) + ERR_HEADER_TRAILING_SIZE)) + (cursor-proof-type (unwrap! (contract-call? .hk-cursor-v1 read-uint-8 { + bytes: pf-bytes, + pos: (+ (get pos (get next cursor-header-trailing-size)) (get value cursor-header-trailing-size))}) + ERR_PROOF_TYPE))) + ;; Check magic bytes + (asserts! (is-eq (get value cursor-magic) PNAU_MAGIC) ERR_MAGIC_BYTES) + ;; Check major version + (asserts! (is-eq (get value cursor-version-maj) PYTHNET_MAJOR_VERSION) ERR_VERSION_MAJ) + ;; Check minor version + (asserts! (is-eq (get value cursor-version-min) PYTHNET_MINOR_VERSION) ERR_VERSION_MIN) + ;; Check proof type + (asserts! (is-eq (get value cursor-proof-type) u0) ERR_PROOF_TYPE) + (ok { + value: { + magic: (get value cursor-magic), + version-maj: (get value cursor-version-maj), + version-min: (get value cursor-version-min), + header-trailing-size: (get value cursor-header-trailing-size), + proof-type: (get value cursor-proof-type) + }, + next: (get next cursor-proof-type) + }))) + +(define-read-only (parse-and-verify-prices-updates (bytes (buff 8192)) (merkle-root-hash (buff 20))) + (let ((cursor-num-updates (try! (contract-call? .hk-cursor-v1 read-uint-8 { bytes: bytes, pos: u0 }))) + (cursor-updates-bytes (contract-call? .hk-cursor-v1 slice (get next cursor-num-updates) none)) + (updates (get result (fold parse-price-info-and-proof cursor-updates-bytes { + result: (list), + cursor: { + index: u0, + next-update-index: u0 + }, + bytes: cursor-updates-bytes, + limit: (get value cursor-num-updates) + }))) + (merkle-proof-checks-success (get result (fold check-merkle-proof updates { + result: true, + merkle-root-hash: merkle-root-hash + })))) + (asserts! merkle-proof-checks-success MERKLE_ROOT_MISMATCH) + (ok updates))) + +(define-private (check-merkle-proof + (entry + { + price-identifier: (buff 32), + price: int, + conf: uint, + expo: int, + publish-time: uint, + prev-publish-time: uint, + ema-price: int, + ema-conf: uint, + proof: (list 128 (buff 20)), + leaf-bytes: (buff 255) + }) + (acc + { + merkle-root-hash: (buff 20), + result: bool, + })) + { + merkle-root-hash: (get merkle-root-hash acc), + result: (and (get result acc) + (contract-call? .hk-merkle-tree-keccak160-v1 check-proof + (get merkle-root-hash acc) + (get leaf-bytes entry) + (get proof entry))) + }) + +(define-private (parse-price-info-and-proof + (entry (buff 1)) + (acc { + cursor: { + index: uint, + next-update-index: uint + }, + bytes: (buff 8192), + result: (list 64 { + price-identifier: (buff 32), + price: int, + conf: uint, + expo: int, + publish-time: uint, + prev-publish-time: uint, + ema-price: int, + ema-conf: uint, + proof: (list 128 (buff 20)), + leaf-bytes: (buff 255) + }), + limit: uint + })) + (if (is-eq (len (get result acc)) (get limit acc)) + acc + (if (is-eq (get index (get cursor acc)) (get next-update-index (get cursor acc))) + ;; Parse update + (let ((cursor-update (contract-call? .hk-cursor-v1 new (get bytes acc) (some (get index (get cursor acc))))) + (cursor-message-size (unwrap-panic (contract-call? .hk-cursor-v1 read-uint-16 (get next cursor-update)))) + (cursor-message-type (unwrap-panic (contract-call? .hk-cursor-v1 read-uint-8 (get next cursor-message-size)))) + (cursor-price-identifier (unwrap-panic (contract-call? .hk-cursor-v1 read-buff-32 (get next cursor-message-type)))) + (cursor-price (unwrap-panic (contract-call? .hk-cursor-v1 read-int-64 (get next cursor-price-identifier)))) + (cursor-conf (unwrap-panic (contract-call? .hk-cursor-v1 read-uint-64 (get next cursor-price)))) + (cursor-expo (unwrap-panic (contract-call? .hk-cursor-v1 read-int-32 (get next cursor-conf)))) + (cursor-publish-time (unwrap-panic (contract-call? .hk-cursor-v1 read-uint-64 (get next cursor-expo)))) + (cursor-prev-publish-time (unwrap-panic (contract-call? .hk-cursor-v1 read-uint-64 (get next cursor-publish-time)))) + (cursor-ema-price (unwrap-panic (contract-call? .hk-cursor-v1 read-int-64 (get next cursor-prev-publish-time)))) + (cursor-ema-conf (unwrap-panic (contract-call? .hk-cursor-v1 read-uint-64 (get next cursor-ema-price)))) + (cursor-proof (contract-call? .hk-cursor-v1 advance (get next cursor-message-size) (get value cursor-message-size))) + (cursor-proof-size (unwrap-panic (contract-call? .hk-cursor-v1 read-uint-8 cursor-proof))) + (proof-bytes (contract-call? .hk-cursor-v1 slice (get next cursor-proof-size) none)) + (leaf-bytes (contract-call? .hk-cursor-v1 slice (get next cursor-message-size) (some (get value cursor-message-size)))) + (proof (get result (fold parse-proof proof-bytes { + result: (list), + cursor: { + index: u0, + next-update-index: u0 + }, + bytes: proof-bytes, + limit: (get value cursor-proof-size) + })))) + { + cursor: { + index: (+ (get index (get cursor acc)) u1), + next-update-index: (+ (get index (get cursor acc)) (+ (get pos (get next cursor-proof-size)) (get value cursor-proof-size))), + }, + bytes: (get bytes acc), + result: (unwrap-panic (as-max-len? (append (get result acc) { + price-identifier: (get value cursor-price-identifier), + price: (get value cursor-price), + conf: (get value cursor-conf), + expo:(get value cursor-expo), + publish-time: (get value cursor-publish-time), + prev-publish-time: (get value cursor-prev-publish-time), + ema-price: (get value cursor-ema-price), + ema-conf: (get value cursor-ema-conf), + proof: proof, + leaf-bytes: (unwrap-panic (as-max-len? leaf-bytes u255)) + }) u64)), + limit: (get limit acc), + }) + ;; Increment position + { + cursor: { + index: (+ (get index (get cursor acc)) u1), + next-update-index: (get next-update-index (get cursor acc)), + }, + bytes: (get bytes acc), + result: (get result acc), + limit: (get limit acc), + }))) + +(define-private (parse-proof + (entry (buff 1)) + (acc { + cursor: { + index: uint, + next-update-index: uint + }, + bytes: (buff 8192), + result: (list 128 (buff 20)), + limit: uint + })) + (if (is-eq (len (get result acc)) (get limit acc)) + acc + (if (is-eq (get index (get cursor acc)) (get next-update-index (get cursor acc))) + ;; Parse update + (let ((cursor-hash (contract-call? .hk-cursor-v1 new (get bytes acc) (some (get index (get cursor acc))))) + (hash (get value (unwrap-panic (contract-call? .hk-cursor-v1 read-buff-20 (get next cursor-hash)))))) + ;; Perform assertions + { + cursor: { + index: (+ (get index (get cursor acc)) u1), + next-update-index: (+ (get index (get cursor acc)) u20), + }, + bytes: (get bytes acc), + result: (unwrap-panic (as-max-len? (append (get result acc) hash) u128)), + limit: (get limit acc), + }) + ;; Increment position + { + cursor: { + index: (+ (get index (get cursor acc)) u1), + next-update-index: (get next-update-index (get cursor acc)), + }, + bytes: (get bytes acc), + result: (get result acc), + limit: (get limit acc) + }))) + +(define-private (cast-decoded-price (entry + { + price-identifier: (buff 32), + price: int, + conf: uint, + expo: int, + publish-time: uint, + prev-publish-time: uint, + ema-price: int, + ema-conf: uint, + proof: (list 128 (buff 20)), + leaf-bytes: (buff 255) + })) + { + price-identifier: (get price-identifier entry), + price: (get price entry), + conf: (get conf entry), + expo: (get expo entry), + publish-time: (get publish-time entry), + prev-publish-time: (get prev-publish-time entry), + ema-price: (get ema-price entry), + ema-conf: (get ema-conf entry) + }) diff --git a/contracts/pyth-store-v1.clar b/contracts/pyth-store-v1.clar new file mode 100644 index 0000000..6b5c217 --- /dev/null +++ b/contracts/pyth-store-v1.clar @@ -0,0 +1,45 @@ +;; Title: pyth-store +;; Version: v1 +;; Check for latest version: https://github.com/hirosystems/stacks-pyth-bridge#latest-version +;; Report an issue: https://github.com/hirosystems/stacks-pyth-bridge/issues + +(impl-trait .pyth-traits-v1.storage-trait) + +(define-map prices (buff 32) { + price: int, + conf: uint, + expo: int, + ema-price: int, + ema-conf: uint, + publish-time: uint, + prev-publish-time: uint, +}) + +(define-data-var timestamps uint u0) + +(define-public (read (price-identifier (buff 32))) + (let ((entry (unwrap! (map-get? prices price-identifier) (err u404)))) + (ok entry))) + +(define-public (write (price-identifier (buff 32)) (data { + price: int, + conf: uint, + expo: int, + ema-price: int, + ema-conf: uint, + publish-time: uint, + prev-publish-time: uint, + })) + (ok u1)) + +(define-public (write-batch (batch (list 64 { + price-identifier: (buff 32), + price: int, + conf: uint, + expo: int, + ema-price: int, + ema-conf: uint, + publish-time: uint, + prev-publish-time: uint, + }))) + (ok u1)) diff --git a/contracts/pyth-traits-v1.clar b/contracts/pyth-traits-v1.clar new file mode 100644 index 0000000..be76581 --- /dev/null +++ b/contracts/pyth-traits-v1.clar @@ -0,0 +1,82 @@ +;; Title: pyth-traits +;; Version: v1 +;; Check for latest version: https://github.com/hirosystems/stacks-pyth-bridge#latest-version +;; Report an issue: https://github.com/hirosystems/stacks-pyth-bridge/issues + +(use-trait wormhole-core-trait .wormhole-traits-v1.core-trait) + +(define-trait decoder-trait + ( + (decode-and-verify-price-feeds ((buff 8192) ) (response (list 64 { + price-identifier: (buff 32), + price: int, + conf: uint, + expo: int, + ema-price: int, + ema-conf: uint, + publish-time: uint, + prev-publish-time: uint, + }) uint)) + ) +) + +(define-trait storage-trait + ( + (read ((buff 32)) (response { + price: int, + conf: uint, + expo: int, + ema-price: int, + ema-conf: uint, + publish-time: uint, + prev-publish-time: uint, + } uint)) + + (write ((buff 32) { + price: int, + conf: uint, + expo: int, + ema-price: int, + ema-conf: uint, + publish-time: uint, + prev-publish-time: uint, + }) (response uint uint)) + + (write-batch ((list 64 { + price-identifier: (buff 32), + price: int, + conf: uint, + expo: int, + ema-price: int, + ema-conf: uint, + publish-time: uint, + prev-publish-time: uint, + })) (response uint uint)) + + ) +) + +(define-trait proxy-trait + ( + (read-price-feed ((buff 32)) (response { + price: int, + conf: uint, + expo: int, + ema-price: int, + ema-conf: uint, + publish-time: uint, + prev-publish-time: uint, + } uint)) + + (verify-and-update-price-feeds ((buff 8192) ) (response (list 64 { + price-identifier: (buff 32), + price: int, + conf: uint, + expo: int, + ema-price: int, + ema-conf: uint, + publish-time: uint, + prev-publish-time: uint, + }) uint)) + ) +) diff --git a/contracts/deps/wormhole-core-dev-preview-1.clar b/contracts/wormhole/wormhole-core-v1.clar similarity index 94% rename from contracts/deps/wormhole-core-dev-preview-1.clar rename to contracts/wormhole/wormhole-core-v1.clar index 21ab2fa..cfe8b42 100644 --- a/contracts/deps/wormhole-core-dev-preview-1.clar +++ b/contracts/wormhole/wormhole-core-v1.clar @@ -6,7 +6,7 @@ ;;;; Traits ;; Implements trait specified in wormhole-core-trait contract -(impl-trait .wormhole-core-trait.wormhole-core-trait) +(impl-trait .wormhole-traits-v1.core-trait) ;;;; Constants @@ -103,12 +103,12 @@ ;; []byte payload (VAA message content) ;; ;; @param vaa-bytes: -(define-read-only (parse-vaa (vaa-bytes (buff 2048))) - (let ((cursor-version (unwrap! (contract-call? .hk-cursor-v1 read-u8 { bytes: vaa-bytes, pos: u0 }) +(define-read-only (parse-vaa (vaa-bytes (buff 8192))) + (let ((cursor-version (unwrap! (contract-call? .hk-cursor-v1 read-uint-8 { bytes: vaa-bytes, pos: u0 }) ERR_VAA_PARSING_VERSION)) - (cursor-guardian-set-id (unwrap! (contract-call? .hk-cursor-v1 read-u32 (get next cursor-version)) + (cursor-guardian-set-id (unwrap! (contract-call? .hk-cursor-v1 read-uint-32 (get next cursor-version)) ERR_VAA_PARSING_GUARDIAN_SET)) - (cursor-signatures-len (unwrap! (contract-call? .hk-cursor-v1 read-u8 (get next cursor-guardian-set-id)) + (cursor-signatures-len (unwrap! (contract-call? .hk-cursor-v1 read-uint-8 (get next cursor-guardian-set-id)) ERR_VAA_PARSING_SIGNATURES_LEN)) (cursor-signatures (fold batch-read-signatures @@ -118,21 +118,21 @@ value: (list), iter: (get value cursor-signatures-len) })) - (vaa-body-hash (keccak256 (keccak256 (get value (unwrap! (contract-call? .hk-cursor-v1 read-remaining-bytes-max-2048 (get next cursor-signatures)) + (vaa-body-hash (keccak256 (keccak256 (get value (unwrap! (contract-call? .hk-cursor-v1 read-buff-8192-max (get next cursor-signatures) none) ERR_VAA_HASHING_BODY))))) - (cursor-timestamp (unwrap! (contract-call? .hk-cursor-v1 read-u32 (get next cursor-signatures)) + (cursor-timestamp (unwrap! (contract-call? .hk-cursor-v1 read-uint-32 (get next cursor-signatures)) ERR_VAA_PARSING_TIMESTAMP)) - (cursor-nonce (unwrap! (contract-call? .hk-cursor-v1 read-u32 (get next cursor-timestamp)) + (cursor-nonce (unwrap! (contract-call? .hk-cursor-v1 read-uint-32 (get next cursor-timestamp)) ERR_VAA_PARSING_NONCE)) - (cursor-emitter-chain (unwrap! (contract-call? .hk-cursor-v1 read-u16 (get next cursor-nonce)) + (cursor-emitter-chain (unwrap! (contract-call? .hk-cursor-v1 read-uint-16 (get next cursor-nonce)) ERR_VAA_PARSING_EMITTER_CHAIN)) (cursor-emitter-address (unwrap! (contract-call? .hk-cursor-v1 read-buff-32 (get next cursor-emitter-chain)) ERR_VAA_PARSING_EMITTER_ADDRESS)) - (cursor-sequence (unwrap! (contract-call? .hk-cursor-v1 read-u64 (get next cursor-emitter-address)) + (cursor-sequence (unwrap! (contract-call? .hk-cursor-v1 read-uint-64 (get next cursor-emitter-address)) ERR_VAA_PARSING_SEQUENCE)) - (cursor-consistency-level (unwrap! (contract-call? .hk-cursor-v1 read-u8 (get next cursor-sequence)) + (cursor-consistency-level (unwrap! (contract-call? .hk-cursor-v1 read-uint-8 (get next cursor-sequence)) ERR_VAA_PARSING_CONSISTENCY_LEVEL)) - (cursor-payload (unwrap! (contract-call? .hk-cursor-v1 read-remaining-bytes-max-2048 (get next cursor-consistency-level)) + (cursor-payload (unwrap! (contract-call? .hk-cursor-v1 read-buff-8192-max (get next cursor-consistency-level) none) ERR_VAA_PARSING_PAYLOAD)) (public-keys-results (fold batch-recover-public-keys @@ -158,7 +158,7 @@ ;; @desc Parse and check the validity of a Verified Action Approval (VAA) ;; @param vaa-bytes: -(define-read-only (parse-and-verify-vaa (vaa-bytes (buff 2048))) +(define-read-only (parse-and-verify-vaa (vaa-bytes (buff 8192))) (let ((vaa (try! (parse-vaa vaa-bytes))) (active-guardians (unwrap! (map-get? guardian-sets { set-id: (get guardian-set-id vaa) }) ERR_VAA_CHECKS_GUARDIAN_SET_CONSISTENCY)) (signatures-from-active-guardians (fold batch-check-active-public-keys (get guardians-public-keys vaa) @@ -272,10 +272,10 @@ ;; @desc Foldable function parsing a sequence of bytes into a list of { guardian-id: u8, signature: (buff 65) } (define-private (batch-read-signatures (entry uint) - (acc { next: { bytes: (buff 4096), pos: uint }, iter: uint, value: (list 19 { guardian-id: uint, signature: (buff 65) })})) + (acc { next: { bytes: (buff 8192), pos: uint }, iter: uint, value: (list 19 { guardian-id: uint, signature: (buff 65) })})) (if (is-eq (get iter acc) u0) { iter: u0, next: (get next acc), value: (get value acc) } - (let ((cursor-guardian-id (unwrap-panic (contract-call? .hk-cursor-v1 read-u8 (get next acc)))) + (let ((cursor-guardian-id (unwrap-panic (contract-call? .hk-cursor-v1 read-uint-8 (get next acc)))) (cursor-signature (unwrap-panic (contract-call? .hk-cursor-v1 read-buff-65 (get next cursor-guardian-id))))) { iter: (- (get iter acc) u1), @@ -295,7 +295,7 @@ (define-private (is-eth-address-matching-public-key (uncompressed-public-key (buff 64)) (eth-address (buff 20))) (is-eq (unwrap-panic (slice? (keccak256 uncompressed-public-key) u12 u32)) eth-address)) -(define-private (parse-guardian (cue-position uint) (acc { bytes: (buff 2048), result: (list 20 (buff 20))})) +(define-private (parse-guardian (cue-position uint) (acc { bytes: (buff 8192), result: (list 20 (buff 20))})) (let ( (cursor-address-bytes (unwrap-panic (contract-call? .hk-cursor-v1 read-buff-20 { bytes: (get bytes acc), pos: cue-position }))) ) @@ -317,17 +317,17 @@ { set-id: new-set-id, guardians: new-guardians })) ;; @desc Parse and verify payload's VAA -(define-private (parse-and-verify-guardians-set (bytes (buff 2048))) +(define-private (parse-and-verify-guardians-set (bytes (buff 8192))) (let ((cursor-module (unwrap! (contract-call? .hk-cursor-v1 read-buff-32 { bytes: bytes, pos: u0 }) ERR_GSU_PARSING_MODULE)) - (cursor-action (unwrap! (contract-call? .hk-cursor-v1 read-u8 (get next cursor-module)) + (cursor-action (unwrap! (contract-call? .hk-cursor-v1 read-uint-8 (get next cursor-module)) ERR_GSU_PARSING_ACTION)) - (cursor-chain (unwrap! (contract-call? .hk-cursor-v1 read-u16 (get next cursor-action)) + (cursor-chain (unwrap! (contract-call? .hk-cursor-v1 read-uint-16 (get next cursor-action)) ERR_GSU_PARSING_CHAIN)) - (cursor-new-index (unwrap! (contract-call? .hk-cursor-v1 read-u32 (get next cursor-chain)) + (cursor-new-index (unwrap! (contract-call? .hk-cursor-v1 read-uint-32 (get next cursor-chain)) ERR_GSU_PARSING_INDEX)) - (cursor-guardians-count (unwrap! (contract-call? .hk-cursor-v1 read-u8 (get next cursor-new-index)) + (cursor-guardians-count (unwrap! (contract-call? .hk-cursor-v1 read-uint-8 (get next cursor-new-index)) ERR_GSU_PARSING_GUARDIAN_LEN)) (guardians-bytes (unwrap! (slice? bytes (get pos (get next cursor-guardians-count)) (+ (get pos (get next cursor-guardians-count)) (* (get value cursor-guardians-count) u20))) ERR_GSU_PARSING_GUARDIANS_BYTES)) diff --git a/contracts/wormhole/wormhole-traits-v1.clar b/contracts/wormhole/wormhole-traits-v1.clar new file mode 100644 index 0000000..ac30477 --- /dev/null +++ b/contracts/wormhole/wormhole-traits-v1.clar @@ -0,0 +1,19 @@ +(define-trait core-trait + ( + ;; Parse and Verify cryptographic validity of a VAA + (parse-and-verify-vaa ((buff 8192)) (response { + version: uint, + guardian-set-id: uint, + signatures-len: uint , + signatures: (list 19 { guardian-id: uint, signature: (buff 65) }), + timestamp: uint, + nonce: uint, + emitter-chain: uint, + sequence: uint, + consistency-level: uint, + payload: (buff 8192), + guardians-public-keys: (list 19 { recovered-compressed-public-key: (response (buff 33) uint), guardian-id: uint }), + vaa-body-hash: (buff 32), + } uint)) + ) +) \ No newline at end of file diff --git a/dockerfiles/stacks-pyth-relayer.dockerfile b/dockerfiles/stacks-pyth-relayer.dockerfile index c45d2e0..0129a49 100644 --- a/dockerfiles/stacks-pyth-relayer.dockerfile +++ b/dockerfiles/stacks-pyth-relayer.dockerfile @@ -4,7 +4,7 @@ WORKDIR /src RUN apt update && apt install -y ca-certificates pkg-config libssl-dev libclang-11-dev -RUN rustup update 1.67.0 && rustup default 1.67.0 +RUN rustup update 1.72.0 && rustup default 1.72.0 COPY ./relayer /relayer diff --git a/package-lock.json b/package-lock.json index 5dc6655..72cbf53 100644 --- a/package-lock.json +++ b/package-lock.json @@ -9,10 +9,14 @@ "version": "1.0.0", "license": "ISC", "dependencies": { - "@hirosystems/clarinet-sdk": "^0.5.5", + "@hirosystems/clarinet-sdk": "^0.10.0", "@stacks/transactions": "^6.9.0", "vitest": "^0.34.1", - "vitest-environment-clarinet": "^0.1.3" + "vitest-environment-clarinet": "^0.2.0" + }, + "devDependencies": { + "@fast-check/vitest": "^0.0.8", + "fast-check": "^3.13.1" } }, "node_modules/@esbuild/android-arm": { @@ -345,12 +349,36 @@ "node": ">=12" } }, + "node_modules/@fast-check/vitest": { + "version": "0.0.8", + "resolved": "https://registry.npmjs.org/@fast-check/vitest/-/vitest-0.0.8.tgz", + "integrity": "sha512-cFrcu7nwH+rk1qm1J4YrM1k4MIwvIHG7MrQUMGizqPe58XsvvpZz0X9Xkx1e+xaNg9s1YRVTd241WSR0dK/SpQ==", + "dev": true, + "funding": [ + { + "type": "individual", + "url": "https://github.com/sponsors/dubzzz" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/fast-check" + } + ], + "dependencies": { + "fast-check": "^3.0.0" + }, + "peerDependencies": { + "vitest": ">=0.28.1" + } + }, "node_modules/@hirosystems/clarinet-sdk": { - "version": "0.5.5", - "resolved": "https://registry.npmjs.org/@hirosystems/clarinet-sdk/-/clarinet-sdk-0.5.5.tgz", - "integrity": "sha512-ZiEE8P2va04hJNTzqUlTMcnaiijytCakF/yNVbIi84qhtc9OWnLPlK7PzTMqquU4Rac3jysg1Nd+YmbNxtqyTw==", + "version": "0.10.0", + "resolved": "https://registry.npmjs.org/@hirosystems/clarinet-sdk/-/clarinet-sdk-0.10.0.tgz", + "integrity": "sha512-LIDMtL+lETB6L0OZ3/6AeS3fYz/sLhB5uEITf78Y41BVOKFqAlTE/t8UkphPK2/WQkqoIvdTHQlFus28Thymmw==", "dependencies": { + "@hirosystems/clarinet-sdk-wasm": "^1.0.1", "@stacks/transactions": "^6.9.0", + "kolorist": "^1.8.0", "prompts": "^2.4.2", "vitest": "^0.34.5", "yargs": "^17.7.2" @@ -362,6 +390,11 @@ "node": ">=18.0.0" } }, + "node_modules/@hirosystems/clarinet-sdk-wasm": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@hirosystems/clarinet-sdk-wasm/-/clarinet-sdk-wasm-1.0.1.tgz", + "integrity": "sha512-HmQQl89YsEEWn431xq3STDgJQDQ1E8d0C458mvylT65Ajj0koEIBoP76mwvWMg6xjvwWKW4hTIc+SlulfI3Cwg==" + }, "node_modules/@jest/schemas": { "version": "29.6.3", "resolved": "https://registry.npmjs.org/@jest/schemas/-/schemas-29.6.3.tgz", @@ -445,9 +478,9 @@ } }, "node_modules/@types/chai": { - "version": "4.3.5", - "resolved": "https://registry.npmjs.org/@types/chai/-/chai-4.3.5.tgz", - "integrity": "sha512-mEo1sAde+UCE6b2hxn332f1g1E8WfYRu6p5SvTKr2ZKC1f7gFJXk4h5PyGP9Dt6gCaG8y8XhwnXWC6Iy2cmBng==" + "version": "4.3.6", + "resolved": "https://registry.npmjs.org/@types/chai/-/chai-4.3.6.tgz", + "integrity": "sha512-VOVRLM1mBxIRxydiViqPcKn6MIxZytrbMpd6RJLIWKxUNr3zux8no0Oc7kJx0WAPIitgZ0gkrDS+btlqQpubpw==" }, "node_modules/@types/chai-subset": { "version": "1.3.3", @@ -458,29 +491,29 @@ } }, "node_modules/@types/node": { - "version": "18.17.5", - "resolved": "https://registry.npmjs.org/@types/node/-/node-18.17.5.tgz", - "integrity": "sha512-xNbS75FxH6P4UXTPUJp/zNPq6/xsfdJKussCWNOnz4aULWIRwMgP1LgaB5RiBnMX1DPCYenuqGZfnIAx5mbFLA==" + "version": "18.18.3", + "resolved": "https://registry.npmjs.org/@types/node/-/node-18.18.3.tgz", + "integrity": "sha512-0OVfGupTl3NBFr8+iXpfZ8NR7jfFO+P1Q+IO/q0wbo02wYkP5gy36phojeYWpLQ6WAMjl+VfmqUk2YbUfp0irA==" }, "node_modules/@vitest/expect": { - "version": "0.34.5", - "resolved": "https://registry.npmjs.org/@vitest/expect/-/expect-0.34.5.tgz", - "integrity": "sha512-/3RBIV9XEH+nRpRMqDJBufKIOQaYUH2X6bt0rKSCW0MfKhXFLYsR5ivHifeajRSTsln0FwJbitxLKHSQz/Xwkw==", + "version": "0.34.6", + "resolved": "https://registry.npmjs.org/@vitest/expect/-/expect-0.34.6.tgz", + "integrity": "sha512-QUzKpUQRc1qC7qdGo7rMK3AkETI7w18gTCUrsNnyjjJKYiuUB9+TQK3QnR1unhCnWRC0AbKv2omLGQDF/mIjOw==", "dependencies": { - "@vitest/spy": "0.34.5", - "@vitest/utils": "0.34.5", - "chai": "^4.3.7" + "@vitest/spy": "0.34.6", + "@vitest/utils": "0.34.6", + "chai": "^4.3.10" }, "funding": { "url": "https://opencollective.com/vitest" } }, "node_modules/@vitest/runner": { - "version": "0.34.5", - "resolved": "https://registry.npmjs.org/@vitest/runner/-/runner-0.34.5.tgz", - "integrity": "sha512-RDEE3ViVvl7jFSCbnBRyYuu23XxmvRTSZWW6W4M7eC5dOsK75d5LIf6uhE5Fqf809DQ1+9ICZZNxhIolWHU4og==", + "version": "0.34.6", + "resolved": "https://registry.npmjs.org/@vitest/runner/-/runner-0.34.6.tgz", + "integrity": "sha512-1CUQgtJSLF47NnhN+F9X2ycxUP0kLHQ/JWvNHbeBfwW8CzEGgeskzNnHDyv1ieKTltuR6sdIHV+nmR6kPxQqzQ==", "dependencies": { - "@vitest/utils": "0.34.5", + "@vitest/utils": "0.34.6", "p-limit": "^4.0.0", "pathe": "^1.1.1" }, @@ -489,9 +522,9 @@ } }, "node_modules/@vitest/snapshot": { - "version": "0.34.5", - "resolved": "https://registry.npmjs.org/@vitest/snapshot/-/snapshot-0.34.5.tgz", - "integrity": "sha512-+ikwSbhu6z2yOdtKmk/aeoDZ9QPm2g/ZO5rXT58RR9Vmu/kB2MamyDSx77dctqdZfP3Diqv4mbc/yw2kPT8rmA==", + "version": "0.34.6", + "resolved": "https://registry.npmjs.org/@vitest/snapshot/-/snapshot-0.34.6.tgz", + "integrity": "sha512-B3OZqYn6k4VaN011D+ve+AA4whM4QkcwcrwaKwAbyyvS/NB1hCWjFIBQxAQQSQir9/RtyAAGuq+4RJmbn2dH4w==", "dependencies": { "magic-string": "^0.30.1", "pathe": "^1.1.1", @@ -502,9 +535,9 @@ } }, "node_modules/@vitest/spy": { - "version": "0.34.5", - "resolved": "https://registry.npmjs.org/@vitest/spy/-/spy-0.34.5.tgz", - "integrity": "sha512-epsicsfhvBjRjCMOC/3k00mP/TBGQy8/P0DxOFiWyLt55gnZ99dqCfCiAsKO17BWVjn4eZRIjKvcqNmSz8gvmg==", + "version": "0.34.6", + "resolved": "https://registry.npmjs.org/@vitest/spy/-/spy-0.34.6.tgz", + "integrity": "sha512-xaCvneSaeBw/cz8ySmF7ZwGvL0lBjfvqc1LpQ/vcdHEvpLn3Ff1vAvjw+CoGn0802l++5L/pxb7whwcWAw+DUQ==", "dependencies": { "tinyspy": "^2.1.1" }, @@ -513,9 +546,9 @@ } }, "node_modules/@vitest/utils": { - "version": "0.34.5", - "resolved": "https://registry.npmjs.org/@vitest/utils/-/utils-0.34.5.tgz", - "integrity": "sha512-ur6CmmYQoeHMwmGb0v+qwkwN3yopZuZyf4xt1DBBSGBed8Hf9Gmbm/5dEWqgpLPdRx6Av6jcWXrjcKfkTzg/pw==", + "version": "0.34.6", + "resolved": "https://registry.npmjs.org/@vitest/utils/-/utils-0.34.6.tgz", + "integrity": "sha512-IG5aDD8S6zlvloDsnzHw0Ut5xczlF+kv2BOTo+iXfPr54Yhi5qbVOgGB1hZaVq4iJ4C/MZ2J0y15IlsV/ZcI0A==", "dependencies": { "diff-sequences": "^29.4.3", "loupe": "^2.3.6", @@ -745,6 +778,28 @@ "node": ">=6" } }, + "node_modules/fast-check": { + "version": "3.13.1", + "resolved": "https://registry.npmjs.org/fast-check/-/fast-check-3.13.1.tgz", + "integrity": "sha512-Xp00tFuWd83i8rbG/4wU54qU+yINjQha7bXH2N4ARNTkyOimzHtUBJ5+htpdXk7RMaCOD/j2jxSjEt9u9ZPNeQ==", + "dev": true, + "funding": [ + { + "type": "individual", + "url": "https://github.com/sponsors/dubzzz" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/fast-check" + } + ], + "dependencies": { + "pure-rand": "^6.0.0" + }, + "engines": { + "node": ">=8.0.0" + } + }, "node_modules/fsevents": { "version": "2.3.3", "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", @@ -795,6 +850,11 @@ "node": ">=6" } }, + "node_modules/kolorist": { + "version": "1.8.0", + "resolved": "https://registry.npmjs.org/kolorist/-/kolorist-1.8.0.tgz", + "integrity": "sha512-Y+60/zizpJ3HRH8DCss+q95yr6145JXZo46OTpFvDZWLfRCE4qChOyk1b26nMaNpfHHgxagk9dXT5OP0Tfe+dQ==" + }, "node_modules/local-pkg": { "version": "0.4.3", "resolved": "https://registry.npmjs.org/local-pkg/-/local-pkg-0.4.3.tgz", @@ -820,9 +880,9 @@ } }, "node_modules/magic-string": { - "version": "0.30.3", - "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.3.tgz", - "integrity": "sha512-B7xGbll2fG/VjP+SWg4sX3JynwIU0mjoTc6MPpKNuIvftk6u6vqhDnk1R80b8C2GBR6ywqy+1DcKBrevBg+bmw==", + "version": "0.30.4", + "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.4.tgz", + "integrity": "sha512-Q/TKtsC5BPm0kGqgBIF9oXAs/xEf2vRKiIB4wCRQTJOQIByZ1d+NnUOotvJOvNpi5RNIgVOMC3pOuaP1ZTDlVg==", "dependencies": { "@jridgewell/sourcemap-codec": "^1.4.15" }, @@ -925,9 +985,9 @@ } }, "node_modules/postcss": { - "version": "8.4.30", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.30.tgz", - "integrity": "sha512-7ZEao1g4kd68l97aWG/etQKPKq07us0ieSZ2TnFDk11i0ZfDW2AwKHYU8qv4MZKqN2fdBfg+7q0ES06UA73C1g==", + "version": "8.4.31", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.31.tgz", + "integrity": "sha512-PS08Iboia9mts/2ygV3eLpY5ghnUcfLV/EXTOW1E2qYxJKGGBUtNjN76FYHnMs36RmARn41bC0AZmn+rR0OVpQ==", "funding": [ { "type": "opencollective", @@ -976,6 +1036,22 @@ "node": ">= 6" } }, + "node_modules/pure-rand": { + "version": "6.0.4", + "resolved": "https://registry.npmjs.org/pure-rand/-/pure-rand-6.0.4.tgz", + "integrity": "sha512-LA0Y9kxMYv47GIPJy6MI84fqTd2HmYZI83W/kM/SkKfDlajnZYfmXFTxkbY+xSBPkLJxltMa9hIkmdc29eguMA==", + "dev": true, + "funding": [ + { + "type": "individual", + "url": "https://github.com/sponsors/dubzzz" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/fast-check" + } + ] + }, "node_modules/react-is": { "version": "18.2.0", "resolved": "https://registry.npmjs.org/react-is/-/react-is-18.2.0.tgz", @@ -1028,9 +1104,9 @@ "integrity": "sha512-1XMJE5fQo1jGH6Y/7ebnwPOBEkIEnT4QF32d5R1+VXdXveM0IBMJt8zfaxX1P3QhVwrYe+576+jkANtSS2mBbw==" }, "node_modules/std-env": { - "version": "3.3.3", - "resolved": "https://registry.npmjs.org/std-env/-/std-env-3.3.3.tgz", - "integrity": "sha512-Rz6yejtVyWnVjC1RFvNmYL10kgjC49EOghxWn0RFqlCHGFpQx+Xe7yW3I4ceK1SGrWIGMjD5Kbue8W/udkbMJg==" + "version": "3.4.3", + "resolved": "https://registry.npmjs.org/std-env/-/std-env-3.4.3.tgz", + "integrity": "sha512-f9aPhy8fYBuMN+sNfakZV18U39PbalgjXG3lLB9WkaYTxijru61wb57V9wxxNthXM5Sd88ETBWi29qLAsHO52Q==" }, "node_modules/string-width": { "version": "4.2.3", @@ -1068,9 +1144,9 @@ } }, "node_modules/tinybench": { - "version": "2.5.0", - "resolved": "https://registry.npmjs.org/tinybench/-/tinybench-2.5.0.tgz", - "integrity": "sha512-kRwSG8Zx4tjF9ZiyH4bhaebu+EDz1BOx9hOigYHlUW4xxI/wKIUQUqo018UlU4ar6ATPBsaMrdbKZ+tmPdohFA==" + "version": "2.5.1", + "resolved": "https://registry.npmjs.org/tinybench/-/tinybench-2.5.1.tgz", + "integrity": "sha512-65NKvSuAVDP/n4CqH+a9w2kTlLReS9vhsAP06MWx+/89nMinJyB2icyl58RIcqCmIggpojIGeuJGhjU1aGMBSg==" }, "node_modules/tinypool": { "version": "0.7.0", @@ -1081,9 +1157,9 @@ } }, "node_modules/tinyspy": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/tinyspy/-/tinyspy-2.1.1.tgz", - "integrity": "sha512-XPJL2uSzcOyBMky6OFrusqWlzfFrXtE0hPuMgW8A2HmaqrPo4ZQHRN/V0QXN3FSjKxpsbRrFc5LI7KOwBsT1/w==", + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/tinyspy/-/tinyspy-2.2.0.tgz", + "integrity": "sha512-d2eda04AN/cPOR89F7Xv5bK/jrQEhmcLFe6HFldoeO9AJtps+fqEnh486vnT/8y4bw38pSyxDcTCAq+Ks2aJTg==", "engines": { "node": ">=14.0.0" } @@ -1107,9 +1183,9 @@ "integrity": "sha512-uY/99gMLIOlJPwATcMVYfqDSxUR9//AUcgZMzwfSTJPDKzA1S8mX4VLqa+fiAtveraQUBCz4FFcwVZBGbwBXIw==" }, "node_modules/vite": { - "version": "4.4.9", - "resolved": "https://registry.npmjs.org/vite/-/vite-4.4.9.tgz", - "integrity": "sha512-2mbUn2LlUmNASWwSCNSJ/EG2HuSRTnVNaydp6vMCm5VIqJsjMfbIWtbH2kDuwUVW5mMUKKZvGPX/rqeqVvv1XA==", + "version": "4.4.11", + "resolved": "https://registry.npmjs.org/vite/-/vite-4.4.11.tgz", + "integrity": "sha512-ksNZJlkcU9b0lBwAGZGGaZHCMqHsc8OpgtoYhsQ4/I2v5cnpmmmqe5pM4nv/4Hn6G/2GhTdj0DhZh2e+Er1q5A==", "dependencies": { "esbuild": "^0.18.10", "postcss": "^8.4.27", @@ -1161,9 +1237,9 @@ } }, "node_modules/vite-node": { - "version": "0.34.5", - "resolved": "https://registry.npmjs.org/vite-node/-/vite-node-0.34.5.tgz", - "integrity": "sha512-RNZ+DwbCvDoI5CbCSQSyRyzDTfFvFauvMs6Yq4ObJROKlIKuat1KgSX/Ako5rlDMfVCyMcpMRMTkJBxd6z8YRA==", + "version": "0.34.6", + "resolved": "https://registry.npmjs.org/vite-node/-/vite-node-0.34.6.tgz", + "integrity": "sha512-nlBMJ9x6n7/Amaz6F3zJ97EBwR2FkzhBRxF5e+jE6LA3yi6Wtc2lyTij1OnDMIr34v5g/tVQtsVAzhT0jc5ygA==", "dependencies": { "cac": "^6.7.14", "debug": "^4.3.4", @@ -1183,22 +1259,22 @@ } }, "node_modules/vitest": { - "version": "0.34.5", - "resolved": "https://registry.npmjs.org/vitest/-/vitest-0.34.5.tgz", - "integrity": "sha512-CPI68mmnr2DThSB3frSuE5RLm9wo5wU4fbDrDwWQQB1CWgq9jQVoQwnQSzYAjdoBOPoH2UtXpOgHVge/uScfZg==", + "version": "0.34.6", + "resolved": "https://registry.npmjs.org/vitest/-/vitest-0.34.6.tgz", + "integrity": "sha512-+5CALsOvbNKnS+ZHMXtuUC7nL8/7F1F2DnHGjSsszX8zCjWSSviphCb/NuS9Nzf4Q03KyyDRBAXhF/8lffME4Q==", "dependencies": { "@types/chai": "^4.3.5", "@types/chai-subset": "^1.3.3", "@types/node": "*", - "@vitest/expect": "0.34.5", - "@vitest/runner": "0.34.5", - "@vitest/snapshot": "0.34.5", - "@vitest/spy": "0.34.5", - "@vitest/utils": "0.34.5", + "@vitest/expect": "0.34.6", + "@vitest/runner": "0.34.6", + "@vitest/snapshot": "0.34.6", + "@vitest/spy": "0.34.6", + "@vitest/utils": "0.34.6", "acorn": "^8.9.0", "acorn-walk": "^8.2.0", "cac": "^6.7.14", - "chai": "^4.3.7", + "chai": "^4.3.10", "debug": "^4.3.4", "local-pkg": "^0.4.3", "magic-string": "^0.30.1", @@ -1209,7 +1285,7 @@ "tinybench": "^2.5.0", "tinypool": "^0.7.0", "vite": "^3.1.0 || ^4.0.0 || ^5.0.0-0", - "vite-node": "0.34.5", + "vite-node": "0.34.6", "why-is-node-running": "^2.2.2" }, "bin": { @@ -1259,14 +1335,12 @@ } }, "node_modules/vitest-environment-clarinet": { - "version": "0.1.3", - "resolved": "https://registry.npmjs.org/vitest-environment-clarinet/-/vitest-environment-clarinet-0.1.3.tgz", - "integrity": "sha512-HExF1X/17ADVo2szTkDmhXLfV8v4UKj6K039OIMfBihBQlLbX4Wg+yrTZiIevn6CCBfwEzjJXz5GOxg+GnOpAg==", - "dependencies": { - "vitest": "^0.34.4" - }, + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/vitest-environment-clarinet/-/vitest-environment-clarinet-0.2.0.tgz", + "integrity": "sha512-0ensLaHs7QlST8SayRfTI6VFP5Xh7gXDKlWXh67OqCtOlNYxJ4wfxnsXf6CcUmNAqPAVHEcbqR5E3IMPZ4SCdQ==", "peerDependencies": { - "@hirosystems/clarinet-sdk": "0" + "@hirosystems/clarinet-sdk": "0", + "vitest": "0" } }, "node_modules/webidl-conversions": { diff --git a/package.json b/package.json index a252ed1..d8fdd73 100644 --- a/package.json +++ b/package.json @@ -14,9 +14,13 @@ "license": "ISC", "type": "module", "dependencies": { - "@hirosystems/clarinet-sdk": "^0.5.5", + "@hirosystems/clarinet-sdk": "^0.10.0", "@stacks/transactions": "^6.9.0", "vitest": "^0.34.1", - "vitest-environment-clarinet": "^0.1.3" + "vitest-environment-clarinet": "^0.2.0" + }, + "devDependencies": { + "@fast-check/vitest": "^0.0.8", + "fast-check": "^3.13.1" } } diff --git a/tests/constants.ts b/tests/constants.ts deleted file mode 100644 index 3428b14..0000000 --- a/tests/constants.ts +++ /dev/null @@ -1,98 +0,0 @@ -export const mainnet_valid_pfs = [ - "01000000030d005b26a5ad9468407b98767abd7148f6d1c02a316b27001c1443a7a104928835e651ea5ef173752cfaa4859db565e16ab3ad6a28bb3793ace209dbad04c5ca7a7001016456ac96ec1439907736135b04b3cac5eeacba3c0d30503f97034ff84301ace93063120f56ed92c7a9707aa2016ae3f9dca4963596de87ba5983b840f796319e010233265dc219325dcff680fc5b632ada2dc5249ef437af22a386bc5caaadd1c66f5c2f7fe418dff397ca8609b3fe0d9b4a9ae08f2923e58095254ce7fbf717811b0103f11b0aec9c6265eea4e666ee5e3b1bf12236439dba0978dc8af738731ea81c416d4a5ca5e99344f95ad9e0e11a0ba7b5210bc4e9c13315251ecd8f2e2e6c006300049faaab5a536bad6e33dc63a643a2a0179744d50ab45e3e005e61d05a09bfb99a7fd3b35e71c8c18e9e45023425e7b145ece170b6872ff1b523664643f531fa01000a104723c46b41d6088675bbbc66704e49592cafd73ced3397d784c7b1ea917e1136a3dbae246ef9ad686a177bd021e82632e379f48e077fba86fb53330de8fcb1010b005daa1a9f81d27e0a8819f30e67d27a5f43e1fb10dfbbc65124007cb464ee331b9597ed11138fed943285fea688207e3132dd974498b76262f35abf3f908928000da0bfde1eb247b3fdeda6282ecbd0aeae0d75383bce4c54c18520d35d515c763517c24bdd9afb6b5f5f00812bc10f86cf98c051415dbd6a0eb6fe31067d06d386000e6782f1714277a13cf2dc13494b503e809ad015b1a78164a4e54bd3e68a2f164568bdfbb1b1a4f220e0b4d15448fe3adfd1fcda48b91869f3fa39f9d537d271d0010feb0b88a3a60bf1e9a451f7aa8839e493b6d919bb30762d1218b777e68cbb48e34f3f1f67b6ccb355bf11f96d1237c7aa8057b5bc7aab2bb1bf0986a23e93349801109063ab9b968b6498d27b4aba8b4c9d5b65f0b1ffc7bb9778868c141436f35820359a0c4935a71cf2bd49d14fbe95f0eb4e9462425a7be5b6188787f5e430b493001152ae6fe0525a7564aa1cb4e8447abc27336d11ff25f7cfb1cd67616b7e30a27a215119da7531947dde0ed4efbca877e824bc203c40f5c8d9bbe7a705155478d20112f10d3eae21de379d4a091bd4162965ac21b3c51aec144be676e803cf78f4846c172d35a17c86dce051f42c1233c69ffa1986a1ee34eafde1a22e5ccd0e42b18d01648b5aad00000000001af8cd23c2ab91237730770bbea08d61005cdda0984348f3f6eecb559638c0bba0000000001c5bd3110150325748000300010001020005009d2efa1235ab86c0935cb424b102be4f217e74d1109df9e75dfa8338fc0f0908782f95862b045670cd22bee3114c39763a4a08beeb663b145d283c31d7d1101c4f0000000573acaada00000000015d02adfffffff8000000056e343a28000000000181eb6201000000170000001b00000000648b5aad00000000648b5aad00000000648b5aac0000000573acaada00000000015d02ad00000000648b5aac48d6033d733e27950c2e0351e2505491cd9154824f716d9513514c74b9f98f583dd2b63686a450ec7290df3a1e0b583c0481f651351edfa7636f39aed55cf8a3000000026a600fd80000000000cb227afffffff80000000268e1b6fc0000000000c37400010000001b0000002000000000648b5aad00000000648b5aad00000000648b5aac000000026a600fd80000000000cb227a00000000648b5aac3515b3861e8fe93e5f540ba4077c216404782b86d5e78077b3cbfd27313ab3bce62df6c8b4a85fe1a67db44dc12de5db330f7ac66b72dc658afedf0f4a415b4300000249acd54e7e000000007b0d5582fffffff800000247ba887a800000000070c4e666010000001a0000001e00000000648b5aad00000000648b5aad00000000648b5aac00000249acd54e7e000000007b0d558200000000648b5aac9b5f73e0075e7d70376012180ddba94272f68d85eae4104e335561c982253d41a19d04ac696c7a6616d291c7e5d1377cc8be437c327b75adb5dc1bad745fcae80000000003922a7d00000000000155d3fffffff800000000038e900a0000000000012a51010000000f0000001100000000648b5aad00000000648b5aad00000000648b5aac0000000003924d74000000000001385800000000648b5aace876fcd130add8984a33aab52af36bc1b9f822c9ebe376f3aa72d630974e15f0dcef50dd0a4cd2dcc17e45df1676dcb336a11a61c69df7a0299b0150c672d25c00000000005d46e800000000000015f4fffffff800000000005cef1c0000000000001543010000001b0000002000000000648b5aad00000000648b5aad00000000648b5aac00000000005d46e800000000000015f400000000648b5aac", - "01000000030d0076f4e3badfe434e501f376a24f84d4636082b6a8fb21bbf5cc8ee7c3a42b2dcc640c4f77027a935aa08ed030b409447452af7fcead6ec24549c03e552b9ed3b201018feee678abc925d9a758445d3efb7a7e58314d166f736f2ce5ec7567ec00295e1307b803dc74d1b567014d27ce5bcc7968c91f3d035abbe8b398afa3334d6a540003a18e6686ea73ada65799e9e0297ee0b00139e04703a6c67449c0b3918fa92c4274fab0f56180f1d9ee1ddbe76acf40937016955addf6e76e8e94aae502d4c0f60004961dc7f51e657961c94a632702e929d12434fb2fb80438f1b1e90a5fefa8d5f102a7c70046652b761a173c78de351587284b378abbadbcb2a44a031f3a3c78b00109375fdcf1472e853682cd7b80ff78818568bea166e752b4cd2334a1f725d2d0c85498ec27055c1a43b6717673c556bf7c4d78efb102d5372a4b009687ab45b9cc010a72426aa3045b64127bf0e765aff8644cceeecb9739fa19acb2d2b0936c4517932882fe0fec0217ad794def938580c15e46459183e723135332d0876ea998b05e010bb32a6f4cf571e8a1f4efeeb9bf97e5fa1399d6f5faaf9c243c5fe3746ffa79f27bda866de8a9ef18052c24d37c355bdc41b9750a94fe339fa4608e58e143991a010cce634e42ea68664dcb59717d58150f032b82654ccf1ef08fd9fd17703d68b7ca5609a8d1f883ca5f2d82eca4ef2fe97f933d76ae28b30295869deba9440af1d8000df19654675ba8bbc6d178a460c353ae9cca7895dd1cfe638882ffcc45d6046dfb6880a6a61d039e1906e5ce87e1e0275a551a74069c6b78544996ed56adc66385000f260c57ebd6f2e526e386436ac2eef3b0305d0de52e28ed876894783129c75f5721be2a30dc9478316b7efd5815c4690350a59ec178990188e4d1cfd525c62a7e00101178b0215e4f6f29fc54924a3bfa123d4037760c22e98cb29b3e2f2a61b3b00f66482446f177b0808ec735558b3488510f06ce5a2dad7839934006ce33015f6a001186b81e3dbeb05e0c31ba68e20e18092375bc62824034758411fb13cecc4bc2c5564ff7404fe8528367a6b06d40bebb3456e897dfbbbb220fcf85b59c3d2c052501129ef799c5a009006015f28f83191cbfe662cf2f1d86c91a286ec048fe6040869515e6fbd4301fc8c3ee31a27e7d5b6b1b95875d17f6f21368ef015837ce582c3401648b5aaa00000000001af8cd23c2ab91237730770bbea08d61005cdda0984348f3f6eecb559638c0bba0000000001c5bd2df0150325748000300010001020005009dd191f2e7d61c0eeeb661e9d07084d395815518e17e4ccc32c9b0bf9086e90a3b1ae76ce538326a90bf3b4f7e37082ce605585e30932e82d112b023ce8c33e01c00000000000000000000000000000000fffffff80000000000000000000000000000000000000000000000001a00000000648b5aaa00000000000000000000000000000000000000000000000000000000000000000000000000000000e7234913d80bf9bf77509d211bf5e73bc67e8abe6b16a4b36d5afb33e1a1a12cec7a775f46379b5e943c3526b1c8d54cd49749176b0b98e02dde68d1bd335c1700000000000000000000000000000000fffffff80000000000000000000000000000000000000000080000001c00000000648b5aaa00000000648b5aaa000000000000000000000000000000000000000000000000000000000000000023941ab3d481fbe313e94a122937f7b4ee29acee62e4d9eb4005afed932b173f8963217838ab4cf5cadc172203c1f0b763fbaa45f346d8ee50ba994bbcac302600000000000000000000000000000000fffffff80000000000000000000000000000000000000000030000001b00000000648b5aaa00000000648b5aaa00000000000000000000000000000000000000000000000000000000000000006b3980174a173c504dc5dd8b3397062db3c67e06f52d53b481e65b6c1c4cc848f9c2e890443dd995d0baafc08eea3358be1ffb874f93f99c30b3816c460bbac300000000000000000000000000000000fffffff80000000000000000000000000000000000000000030000001a00000000648b5aaa00000000648b5aaa0000000000000000000000000000000000000000000000000000000000000000b19ce5c168c433e14e522c6918f2eee1450ac96ff06c7a58acd158f9af100c929f520bbc39aad043e3bbb46b05079222d63bd94c150205f2a8889b6b36a0f37600000000000000000000000000000000fffffff80000000000000000000000000000000000000000040000001b00000000648b5aaa00000000648b5aaa0000000000000000000000000000000000000000000000000000000000000000", - "01000000030d00221ee8e172ff8113223b83cc7eedf280b1dcacc37df5a024116d1a940b12fb5b76bb2535dfbd8af8c38239514381ae37cf2062aefdb41b196dd5c6dcfc94361d01021ba85a74a8a60e7ebfe20721b8e733d2fb853af98684b5c193cc3f9b20c757ad651326d6b135a4838b113adaae462f387c3c2afb249f1f882c770903495e8b4701033b9d2e19502e2f415cef825c15c3f6cc9de749564587ffa23bae53bbadf0676e3dec7e3bccca30c69d76300ef07849cc70677d5d5dcabbea61b912bf91ca7d290004f9c224e447d697229753d9fb5bc617762f7a5248e1fca13ac605a5371f19ddc46268331d4c2d197d80db0cf6c2b7f9203eb08c193dfd928018c0592c227f4cd8010839b7e8f6b4a7e7a2bfd058742a0e237f4d2bfb768c9df35fada8a6ddf18e04d05fbddc91cc53d429fbc2265836885e4ac251c3cad49a94816cf2e09bb394c627000a770a381d7368b7e8abb51fc619f0c56c0fdd261f99ce553d3160f2a669e7f27043d3b30e53c8a2b40ef7f11d8140b201bbeaf4bc03c5b6d9655292a71bd1e423010be6b61ad60af4e8fec918ba4a2ca11fcb7cf94035a54a0ced906ae7c2e77ae17d6cdeac4a511a2127da22869694a9c5804d3cd5eca4cfd39daa587091f85b9d98010cdc14d6ff68b64a99b09774fdbf30509f8767fd42ac511b08dc68e53d6a6eea875c53d272274e20f7fae47f6ce386a53c6c1c18243ec0ea2c6b29074b9b14df57000ddf03bb2a85f8c7dd7d4c771cdf503e294a0f2c92d3dad87dcdeb0b9455f16405167f5042b5dbe12e553570f43fa86eb720ce2fe70927b9c5e7bba70bcd2583ce010f62c6b4a1a0ca48542d35238caf9ea9fa3f6265dc81072773cf5bac9349c4fb5f179fb0cf7f31e73918894d43af0ca359d2458fdae2484271bc72c0766a2c42b90110ec732ead991211c67e1c44e92d37666abe7a24a788f9a0619bb536529b243251522ec65e847b9640c36c8336e8e4ede3f7e0718c1b60a0c15afdccf9ce0ecad90111106ce16dc68177e4a3daa28218b97f132f6361beaecfca4431227910fcd91e8e02473a424114696d21d7cbd3b5287cf83c4a6e60b7f158b2a39112ab37197c59011241b470bcd3d560fefee3ad56c1478813ae54ee75afb1ffd608e8e550c6ee1e2c6ccbd3cff9b56580b7ed5d36794876559aabe5dc06bfaca16322bf71d891b18701648b5ab900000000001af8cd23c2ab91237730770bbea08d61005cdda0984348f3f6eecb559638c0bba0000000001c5bd4140150325748000300010001020005009d2efa1235ab86c0935cb424b102be4f217e74d1109df9e75dfa8338fc0f0908782f95862b045670cd22bee3114c39763a4a08beeb663b145d283c31d7d1101c4f000000057358408000000000019c682cfffffff8000000056e39ad78000000000181e6fb01000000170000001b00000000648b5ab900000000648b5ab800000000648b5ab8000000057358408000000000019c682c00000000648b5ab848d6033d733e27950c2e0351e2505491cd9154824f716d9513514c74b9f98f583dd2b63686a450ec7290df3a1e0b583c0481f651351edfa7636f39aed55cf8a3000000026a02c1200000000000af79e0fffffff80000000268e319ac0000000000c36fb4010000001b0000002000000000648b5ab900000000648b5ab900000000648b5ab8000000026a02c1200000000000af79e000000000648b5ab83515b3861e8fe93e5f540ba4077c216404782b86d5e78077b3cbfd27313ab3bce62df6c8b4a85fe1a67db44dc12de5db330f7ac66b72dc658afedf0f4a415b43000002498cb46220000000007c363a70fffffff800000247bc41ea500000000070d2a8aa01000000190000001e00000000648b5ab900000000648b5ab900000000648b5ab8000002498d5dbae2000000007b8ce1ae00000000648b5ab89b5f73e0075e7d70376012180ddba94272f68d85eae4104e335561c982253d41a19d04ac696c7a6616d291c7e5d1377cc8be437c327b75adb5dc1bad745fcae8000000000391efa300000000000102e3fffffff800000000038e941e0000000000012a15010000000f0000001100000000648b5ab900000000648b5ab800000000648b5ab8000000000391efa300000000000102e300000000648b5ab8e876fcd130add8984a33aab52af36bc1b9f822c9ebe376f3aa72d630974e15f0dcef50dd0a4cd2dcc17e45df1676dcb336a11a61c69df7a0299b0150c672d25c00000000005d42220000000000001272fffffff800000000005cef740000000000001542010000001b0000002000000000648b5ab900000000648b5ab900000000648b5ab800000000005d436d000000000000119c00000000648b5ab8", - "01000000030d008ab5f79662dcfaad986e71d9fd8b8af862353fbc84f6bfe13bd41cb01d407bfb1c21bc8464b7919257216ad80c84b5a884253cd1cdb8be5bce072db961d786dd00014662da52a53ad933f432f6b5df809a33ffdf41a8ca9c03b69b543e83d772b18b341a70952ffa79b7132e018665b5324953e81169fe918285363350447910b96a01022b65866f375006eba69a3fdc5d68519771c2ba1b7bf0df117d4a164b4390ffaa13c36f647db1ba1c4e2856c846cd5ecd1a6d52d79d3fcdc29ffcf8912bb8a8b60103455afa6aaad3830d2de7b5c78754f6fad20136f6cf16a63ec566ec7e21f5ec1414722357823158589025e17c1a3a1c8f4f4801a5713bbc0f0ab42b5dabef29f300041ec4cd4ca407fc5fd80c1a60cd0ecdef3a12fabe71ace1e1d2babb8594694f4143b536ac74061b81535d597c59dbe98e771888823a6b2c2ec7e4a5efe63116e60108d4eded020cce69cfaee4d72020a46bfbf913fbec2092a6da1dfc1777976aef4c32c840cdc23de32ecd4fe7dc6a5b0178fcc471b63fefd1e299cf65eb678dd28c010af6f5eaf19e7acabb087301ce2c2c93699b9fb4e3e8932a7dcce9c100d0250a45303042f939c5cf880df3770cfae74fbbbccf9ac00fcffd3344e76af9b2122e89010b10789636a4204b59ff0f6700915a39e3b185e2f2b36e13898579a10ceb61bddb2237dd311665b0938962b0b02e38a434931c9102dab69108494ffb8e6c6f86e4000c7c3784becffef400d3d21b52ff6d7f18123200f0059f7147a3a1f39f3f602ee31c5549e5058b5f00d0f9225f79a1a53160a01beb6dd1bf58186325f293a816fc000d389231ce8f5ffddfc4dcb198f64aab2426d3820b2c0496966f115867abdb5ba200e7a81701e49c8a66a7f1e646fd97d3a20b5931cebd39d564f0704898410372000fe702cd49563343b099894c4036ae43d99a48aefb25726037acd4072cca092d9f52144d22f66739f85bfec1dcca4ffbc2b160f216e897c9b329ff6f179cf3bb5e0111d73b1e9cdc60348a5524ba4ba619049e381a38a9b800bc578b0d64981db8eb405dc5b4ef4d97d9b31e3b717de5f485e3d384fc7e9a1fadcad971fe26bfb747070112780e544d00cf6181147f2bc67eb908943581952960d9a40f9063162000e958d460dcae60a5720857423b57113ae0a045d5f8755129b09c4643a58b7ff47b090a00648b5ab900000000001af8cd23c2ab91237730770bbea08d61005cdda0984348f3f6eecb559638c0bba0000000001c5bd40f0150325748000300010001020005009dd191f2e7d61c0eeeb661e9d07084d395815518e17e4ccc32c9b0bf9086e90a3b1ae76ce538326a90bf3b4f7e37082ce605585e30932e82d112b023ce8c33e01c00000000000000000000000000000000fffffff80000000000000000000000000000000000000000000000001a00000000648b5ab900000000000000000000000000000000000000000000000000000000000000000000000000000000e7234913d80bf9bf77509d211bf5e73bc67e8abe6b16a4b36d5afb33e1a1a12cec7a775f46379b5e943c3526b1c8d54cd49749176b0b98e02dde68d1bd335c1700000000000000000000000000000000fffffff80000000000000000000000000000000000000000080000001c00000000648b5ab900000000648b5ab8000000000000000000000000000000000000000000000000000000000000000023941ab3d481fbe313e94a122937f7b4ee29acee62e4d9eb4005afed932b173f8963217838ab4cf5cadc172203c1f0b763fbaa45f346d8ee50ba994bbcac302600000000000000000000000000000000fffffff80000000000000000000000000000000000000000030000001b00000000648b5ab900000000648b5ab800000000000000000000000000000000000000000000000000000000000000006b3980174a173c504dc5dd8b3397062db3c67e06f52d53b481e65b6c1c4cc848f9c2e890443dd995d0baafc08eea3358be1ffb874f93f99c30b3816c460bbac300000000000000000000000000000000fffffff80000000000000000000000000000000000000000030000001a00000000648b5ab900000000648b5ab80000000000000000000000000000000000000000000000000000000000000000b19ce5c168c433e14e522c6918f2eee1450ac96ff06c7a58acd158f9af100c929f520bbc39aad043e3bbb46b05079222d63bd94c150205f2a8889b6b36a0f37600000000000000000000000000000000fffffff80000000000000000000000000000000000000000040000001b00000000648b5ab900000000648b5ab80000000000000000000000000000000000000000000000000000000000000000", - "01000000030d00efe9314b41350a515cdb08b4c0055e9a8e7748a106d3c01439205afd8a22502a635122818cd5ae89eab68906bbc4f3d296eda7183aafdd8c73179315a2fafa920101b769624886bbf5d33f54ffe56d7cd233ad58f52c365e613d87864b08c039cc9b47dadfeff561d68b4ab18f37163a35567119d5ca9311a2d640cfc4df96025f420002db2a89cb70c6048a8f545b4ef163eb9d99c84b0a064031d4d70a1ef13194800c533d0171135b781a7237aaed56b5721c86aeaf04683017cde445721a0429176a0103db7ab0abdca5c30d695ac34fbdbb8bbf9dfdb85874871739c94f72de788ba730210688624727b9b41aadf571d8e9e06b3de1074adaebd6d247010c9f22f6637801040588a0cc61f3a2781fa9b31fb297e0fbf27cc59c5634fb66434faec64b7573ba4e4cb0ea17fba1daba1f05e70c5cd80600b257e489d4cd8f73a87a9027077876000a88efeb0348264fd12f7ab0365cc963a303252b7e69182384e7c8f1c5b70484a027bedccd826b7288e16a4462980ad68e239ed17ef3604e3adfc92d86d38c3bf2000bc53bc144339a737cb953f63f89664c3389bb00fb70bfaa77701848207aada1ed2349e0058c0cd72bd0ec63c74b2f6bc6dca4953c0fbac25584ec14e0b7ade0fd000c9c97ff3e448175f23cbf63dee6547f285fc990cf46682845f70a670540856a5064d52ead743cd8beac07ad8216f1a91677d9792c6d3cec5e8f19ac65ad030b14000d1c38365533858ebe968c50e14a3fc500b6e19c385993a887f2d54766ccfec7976283dd2c894e8c8ba6361380e73c7929d6580d96cf6cfd460844f8be089fed2c010fac714c0b78f42322992261052bd3d13fbb5fdd89ea7c43218fa61dc3c05384d53e6edd7833b15e821b18944e1ef724fc57e2a073b121f00c1ff81f6cea0df44f0110188bccd5de8e1c602f1a75d5363b99c56c78de421bc56691d871fed8ea45716a39e14bb3eee6021b933c13700eaff0bf320767cbbb324f0bea02a82363fe68dd0011b52b49d13bfc2b95c178f57c2e06e2f189edcc4611d6c16f43fbbd9065d12531598a48e1725d87e14ed5ae79abb27fda10c88d9b6fa238afd89477eadeaa504600123feb36283522a03e8bceca34cbd0f73ff775ba3344e6635e949c88c3c7a550956d90b04ec0fee272b48e3842b15321ab8bd264993e9d75ab54e524dbe564f1ea00648b5ac500000000001af8cd23c2ab91237730770bbea08d61005cdda0984348f3f6eecb559638c0bba0000000001c5bd5160150325748000300010001020005009d2efa1235ab86c0935cb424b102be4f217e74d1109df9e75dfa8338fc0f0908782f95862b045670cd22bee3114c39763a4a08beeb663b145d283c31d7d1101c4f0000000572aed43c0000000001ad3236fffffff8000000056e3df018000000000181fdf001000000170000001b00000000648b5ac500000000648b5ac400000000648b5ac40000000572aed43c0000000001ad323600000000648b5ac448d6033d733e27950c2e0351e2505491cd9154824f716d9513514c74b9f98f583dd2b63686a450ec7290df3a1e0b583c0481f651351edfa7636f39aed55cf8a30000000269ea264d0000000000c89bb1fffffff80000000268e428600000000000c36501010000001b0000002000000000648b5ac500000000648b5ac400000000648b5ac40000000269f2a49d0000000000c01d6100000000648b5ac43515b3861e8fe93e5f540ba4077c216404782b86d5e78077b3cbfd27313ab3bce62df6c8b4a85fe1a67db44dc12de5db330f7ac66b72dc658afedf0f4a415b4300000249634be7cb00000000823b16b7fffffff800000247bdba59800000000070d9aae2010000001a0000001e00000000648b5ac500000000648b5ac400000000648b5ac400000249634be7cb00000000823b16b700000000648b5ac49b5f73e0075e7d70376012180ddba94272f68d85eae4104e335561c982253d41a19d04ac696c7a6616d291c7e5d1377cc8be437c327b75adb5dc1bad745fcae8000000000391f86b0000000000010c0bfffffff800000000038e97bd00000000000129e4010000000f0000001100000000648b5ac500000000648b5ac400000000648b5ac4000000000391f86b0000000000010c0b00000000648b5ac4e876fcd130add8984a33aab52af36bc1b9f822c9ebe376f3aa72d630974e15f0dcef50dd0a4cd2dcc17e45df1676dcb336a11a61c69df7a0299b0150c672d25c00000000005d3c330000000000001285fffffff800000000005cefc7000000000000153e010000001b0000002000000000648b5ac500000000648b5ac400000000648b5ac400000000005d3c33000000000000136300000000648b5ac4", - "01000000030d00a8d61b80eb325c2b34be51f5e02d6bc28a31c290fc2158d970310c7b8b9354ef27d956ba2d01a349bdf60699d5ef946edeaafc1f5021713e8ab0fdc8a8a9b30b000213e8596ab484000d8b23df322782ab2c2b53414b8a0f726f74e00907567861dd60c7df8ab13fb11cfc52e0ccdbdb47e646ab999c9436414198b99c746f0f022c0103483e492d5d0be7d84f9fbb7a6ec84dd3c3a98583219c185aa65148cdcfa06abc2149d445383cfe177f3e3f34550c7f790328e110bbb91889671b9084e229eab60104563134e3deba6c6dc998aafbc0d41a2877b339e1e3e89d261a43838b35ddfc7831236a76e96652e3f950cf936d1a5d190f205b52f6c96c32998d5d4cc6e9a244010a7aa3abd9a41221db19540d6f3cfdead2a7791f97b6501e1e7a20daa719bf571c7608ffac59c4a40ecd5cdbf68d3d7909d52eb4eee4b99edb3bc7532e33ef747a000be4fa3030ac6e16df839260912300e32abefde86d0d6cb1cbd2da8618f68a8aa055a082b91a96ccbad7a3dbe91c1a9ea408782161d75be03d3d7946bbc0ddcbdd000c99d8a7785ab49118e62c3798cc776455d75e6cafdebdf1cacf74c3b35349493255727db49d158b1c4ff6d599c59e065a8787fc6a28a32592c952f6323e521edb010d9a0d2314f5579279f4cd22d9276bac88e1da3065fdbbe797e6aea426c391eda859e8d2af1456c0d63c763c20dc7d29b95f30586d228b2cf7416077f415c8ae8f000e6520ab9d85bec828b20b9285ff3a4b42d0e8a16355949379592fe87ffe9faa2809ac052817d28f03a64c99cb28d2f7eb14cbceebda3a44ad9ab9c334e0bf6ea0000f350c592188634248d13dffdcd01ad79f35ea400635f3a8fcd43141cd3d22e36425cf8e2072caec9e4a1c6c8d652c8c80db13a3de99a47aab2d993426e9457fa201103e13dd0e3842cae5700354ff5014a408e5aae26c3058eb448b7e6126c9ddef89105fa08de28c390d92e8a57a1cb73ba12c673a287476c9fe6ba22f7fcc7ff252001170801ceed606f2b4b67756c2181d8335a433456416a3eb3348a91f1c39da627971fa4630f88ae22f46e6aec8654c1877c5fb24e6d9f3046b852a2339cb9b7d410012a85acc87e227180d7b9291009a2dc4846f3c5f17f476a9616ef908f07338eb457ed3bf996c63be891a9dc48f1d7294a9d87eb12ee91566a86e061442d71f7ac900648b5ac300000000001af8cd23c2ab91237730770bbea08d61005cdda0984348f3f6eecb559638c0bba0000000001c5bd4f50150325748000300010001020005009dd191f2e7d61c0eeeb661e9d07084d395815518e17e4ccc32c9b0bf9086e90a3b1ae76ce538326a90bf3b4f7e37082ce605585e30932e82d112b023ce8c33e01c00000000000000000000000000000000fffffff80000000000000000000000000000000000000000000000001a00000000648b5ac300000000000000000000000000000000000000000000000000000000000000000000000000000000e7234913d80bf9bf77509d211bf5e73bc67e8abe6b16a4b36d5afb33e1a1a12cec7a775f46379b5e943c3526b1c8d54cd49749176b0b98e02dde68d1bd335c1700000000000000000000000000000000fffffff80000000000000000000000000000000000000000080000001c00000000648b5ac300000000648b5ac3000000000000000000000000000000000000000000000000000000000000000023941ab3d481fbe313e94a122937f7b4ee29acee62e4d9eb4005afed932b173f8963217838ab4cf5cadc172203c1f0b763fbaa45f346d8ee50ba994bbcac302600000000000000000000000000000000fffffff80000000000000000000000000000000000000000030000001b00000000648b5ac300000000648b5ac300000000000000000000000000000000000000000000000000000000000000006b3980174a173c504dc5dd8b3397062db3c67e06f52d53b481e65b6c1c4cc848f9c2e890443dd995d0baafc08eea3358be1ffb874f93f99c30b3816c460bbac300000000000000000000000000000000fffffff80000000000000000000000000000000000000000030000001a00000000648b5ac300000000648b5ac30000000000000000000000000000000000000000000000000000000000000000b19ce5c168c433e14e522c6918f2eee1450ac96ff06c7a58acd158f9af100c929f520bbc39aad043e3bbb46b05079222d63bd94c150205f2a8889b6b36a0f37600000000000000000000000000000000fffffff80000000000000000000000000000000000000000040000001b00000000648b5ac300000000648b5ac30000000000000000000000000000000000000000000000000000000000000000", - "01000000030d00f178d45b9d3ad2d9fa81ed97486b98df8e42a92036b20564dc6dbdf2c7f9981c799cf4eb5def8d00d4722f1feb812840b4f16109331e18dcebb1359c2a31db710101b2ea2219eca61627b59e21562e26af975d47958822ea1932d0b9f9e79ac2878434721b8ca1fc72012a70b1aaddc551cfb141cd367576927a32597e0293ae364f0002f84de3103f961565b76516cb14f57624a98bbc34f2bb5b4c1bb87d927d84056b04e18d6b103322e960a7f9fca8ed29747467ee245f3e89d748221c96c1c7907b000367f957ea177cc69c3c805bd7ae2428fbeb0ada04f496284976cef795a278deb95d6cb1108a07ce78a04225ec78ee242f67abfdf46b8fb9412cb1db8ce70b449b00046cc31156255356d335dab76df9c07b749b3490edab45dfb579408aca756b6c25088d357f90fad7a6d720cbe8cb741ab0367cb0dff13bff42a171ca6d02c0c5c7000855bc88c18b4146ab7da3543075d3b70e0ecaafb5b3169b4834e3024825e6811148ee751e5b962c6f8366f0e225912ee79a60e4fac0a3be0fae8140076af2f9fc010af8805ed0b86b796634735b53c6bcfb6e65e167ad5de1cf9bbfef7f21d03747a80d4b9ba9397e5ac352f7b8e4f208300a0b3fc57dfff5a0d7d6d90a95c1c0ee01010bf55b74837e453b7435f8a033bc6af8a63f932f8c88c10011102e917add7b4a651c772ce350b7ec5c924023b41fdd0ebbe0b1ed28338859dcf498f54a7698c93a000c19683a41f5a6ea4ce9c6b0ae7469d06ec64deb6f15947532e804dc4af4422c3e73cd74740b7dd52cde6491e90b56eb8c03f5326dd7713d20484b1a060e1b5419000dfa7329fa30a534b8b245d8a6cbc8d22862f05211cbab68c995fc6df451f02c757476e23031c13cc234d910fb3c1d1d8bcdbe97b94369e0f4d3c71d6ddd2238b7010e49d90fc4ea8f1d0d845d2765cd1efdf1d3764a7800bae6513f3441dd5d20151f10bc49bc08290b89178df30c8f74f8cdb876515dfb6a6c128e9c59e5cf241c950110ad8e69b349a9c94b7370259418731f774d6676d76c2c77685bdefa07d1a85b1520698b8de553d173fe0d32902eca18b84a75300205f7d316f7abc1c0622bd6e40012fc85d1186428bc1a6ce793e8dc1df41df30369df2103b5f116b4bf435de14a5c05d2bd233fb631190a05447804895eff891fed7a28693255b2e129e3bcfa492b00648b5ad100000000001af8cd23c2ab91237730770bbea08d61005cdda0984348f3f6eecb559638c0bba0000000001c5bd6120150325748000300010001020005009d2efa1235ab86c0935cb424b102be4f217e74d1109df9e75dfa8338fc0f0908782f95862b045670cd22bee3114c39763a4a08beeb663b145d283c31d7d1101c4f0000000572b152200000000001aab452fffffff8000000056e421508000000000182124201000000170000001b00000000648b5ad100000000648b5ad000000000648b5ad00000000572b152200000000001aab45200000000648b5ad048d6033d733e27950c2e0351e2505491cd9154824f716d9513514c74b9f98f583dd2b63686a450ec7290df3a1e0b583c0481f651351edfa7636f39aed55cf8a3000000026a072c600000000000ab0ea0fffffff80000000268e545880000000000c35949010000001b0000002000000000648b5ad100000000648b5ad000000000648b5ad0000000026a072c600000000000ab0ea000000000648b5ad03515b3861e8fe93e5f540ba4077c216404782b86d5e78077b3cbfd27313ab3bce62df6c8b4a85fe1a67db44dc12de5db330f7ac66b72dc658afedf0f4a415b4300000249707b58c0000000007707cd40fffffff800000247bf4e18e00000000070de4b1a010000001a0000001e00000000648b5ad100000000648b5ad000000000648b5ad000000249707b58c000000000745595ec00000000648b5ad09b5f73e0075e7d70376012180ddba94272f68d85eae4104e335561c982253d41a19d04ac696c7a6616d291c7e5d1377cc8be437c327b75adb5dc1bad745fcae80000000003931e320000000000012c1efffffff800000000038e9ba700000000000129e7010000000f0000001100000000648b5ad100000000648b5ad100000000648b5ad0000000000392c7eb000000000001826500000000648b5ad0e876fcd130add8984a33aab52af36bc1b9f822c9ebe376f3aa72d630974e15f0dcef50dd0a4cd2dcc17e45df1676dcb336a11a61c69df7a0299b0150c672d25c00000000005d37ac000000000000136ffffffff800000000005cf013000000000000153d010000001b0000002000000000648b5ad100000000648b5ad100000000648b5ad000000000005d3749000000000000137800000000648b5ad0", - "01000000030d0035a94926588f79b1dfce264c6915258819fa68de2f969d04e38c447d6c03523835519ca606093c83cf8e5b891da5f5765eef06c6fefdcf440112ed65ac5ef36601013e9b1d7d2ad19b560214ae65efa3bdf9d003df3f5d905d3b52de10c728d3402e5f4a169e090db71ee1c418c5e403b46d09808901020326c9540b2ee2f869b55900025d9e0c873f6503c9440f8f6959c11b5785395fd85dde27c8b3dd30c4cff51e672ea9457b2eb233e19a08bacba40fbc4334cbe0140e4d9856bd367d627b13c9b10003db1b6d886b7d80bfcedd9b719ec71ce7b5aff03fb5a0c754190d431df6d7bd615786f99a63f7974edccdeb6686e5ec50234bbc6f785cbc115d847b8676e48c880104878ec42ad0c4430f0b298e426d1f0c12194d37a05f3f166fd8ae09da3f2ea3b326a5f3974fe23180b9cc4a0a229278798dfe5e6084942f887047f6199d0db1ca010a92bffd4605ad6066ec532758e599722b4eba5b89536c4025b25b3404834a45d204f8b6a34724b066810768683a794a9782d7145b73e068fc07e237fcd7a10a08010b27cfdf49b87e744421c4577f87a18d5a68860fa600ed1f2dfb55a8bb21dfbbf26da8d18c9062be4527edd0d01028ac8384e51ae1405a4c51a3d3ba98f96ef510010d9919bfbec41e683fe4a41d52aba2bbdf666c7328452287237001be6d169133df3a499ee8e3b3183aabf780734c0df917767a2be5b75a2129a47e66ff9fbbf839000eb5d28032e4ee8bfd376a976b65ede1e8053add16d63a1955620b7383299870585906efbc4becd3cbcd66de307f8bac1c9f740aaa181cf91d445ba1708c974941000fcd5a3f67c7851db812ee7ff227d0dfd4f4c38aa083b7476a68b92853384267a84d771fd87af6a36237480aefa610f92b42e62b1f489c1bf1889376c4a34d70060010da9559f4ca3a7b2c4fba904650996303135af2cd9c32f822706336c95de490216feea8f47a3ed8e821c8a9102a6fc32a4a6cdb5fdcc552b857828e46d230d4600011c4ea1eeb4fa9a174cb7b7b768a4ce53f148bbaec5e80d570731dd9bb91cb245f30f6dafbdb59e0367321e8a5ed7005ea965ef0a4fd4c709f222ca9d36d5f71d70012ec3184202a29a92ecfc80790df895dee37a3b62ba509fd191599784233d31d72090961d5515f30f10f206c61e51786f9a237bfafc192ef91e53485c27a1568a100648b5ace00000000001af8cd23c2ab91237730770bbea08d61005cdda0984348f3f6eecb559638c0bba0000000001c5bd5d40150325748000300010001020005009dd191f2e7d61c0eeeb661e9d07084d395815518e17e4ccc32c9b0bf9086e90a3b1ae76ce538326a90bf3b4f7e37082ce605585e30932e82d112b023ce8c33e01c00000000000000000000000000000000fffffff80000000000000000000000000000000000000000000000001a00000000648b5ace00000000000000000000000000000000000000000000000000000000000000000000000000000000e7234913d80bf9bf77509d211bf5e73bc67e8abe6b16a4b36d5afb33e1a1a12cec7a775f46379b5e943c3526b1c8d54cd49749176b0b98e02dde68d1bd335c1700000000000000000000000000000000fffffff80000000000000000000000000000000000000000080000001c00000000648b5ace00000000648b5acd000000000000000000000000000000000000000000000000000000000000000023941ab3d481fbe313e94a122937f7b4ee29acee62e4d9eb4005afed932b173f8963217838ab4cf5cadc172203c1f0b763fbaa45f346d8ee50ba994bbcac302600000000000000000000000000000000fffffff80000000000000000000000000000000000000000030000001b00000000648b5ace00000000648b5acd00000000000000000000000000000000000000000000000000000000000000006b3980174a173c504dc5dd8b3397062db3c67e06f52d53b481e65b6c1c4cc848f9c2e890443dd995d0baafc08eea3358be1ffb874f93f99c30b3816c460bbac300000000000000000000000000000000fffffff80000000000000000000000000000000000000000030000001a00000000648b5ace00000000648b5acd0000000000000000000000000000000000000000000000000000000000000000b19ce5c168c433e14e522c6918f2eee1450ac96ff06c7a58acd158f9af100c929f520bbc39aad043e3bbb46b05079222d63bd94c150205f2a8889b6b36a0f37600000000000000000000000000000000fffffff80000000000000000000000000000000000000000040000001b00000000648b5ace00000000648b5acd0000000000000000000000000000000000000000000000000000000000000000", - "01000000030d00ad5cfb79320e3f1c3d0929ac96b7db51ab7dfa5813ea8f1a8d4614dae430c7b041115f958622235a4af5e8e8513ed4acc1e3640610e6d433252ed01fcdc9338301011c653dda631cc45adb2b422382c11fd87b724f97a05ce3ec4dc038390f77fa5108c2132e6d5375e90e03aa8988223d167a2dee3286073cf7d3eeab468f3f8a0a0102ae87fa528ba7881a716223f356f3a246b1138c8a94bb5a8be8c1dea235909f716e7fe41d6d8014928895a2ac88b0481135eaad04f57bceaf51e64b46786496bf0003de49e30959d737517d6bb17ef6a2c8475ccb356db080f2fff222b4e2b4b77c9b6505052eea218353ade471d90f519fc84dc12ea4574429eaf50023ea5401ee1e00048ca6369ea9b87e8474e120c6093d9378bb0720e8114f57e52aac992c82627f864f9432bf0f1edbe3302d0abc27887c1b889b4fa81e013bc0eb14193f4e96854c010831be80ded7a9eed3cfec2f5da4cc7a88f823a06fa543e8d9aa2ada382f758a41173f2057c0287521c5378067cbc98f61b0f31bcbe500894dd5c8540cc5f3ab6e0109055ce3838a27258c67028aba8ac3f4289c33b4e6e371bded5f9404d07ff422306b0fc4dc022d36968e18e4922461baafb322ccaae359ef69f11c733cf13a1100000adf759df8103523f9e812373a43d4a02ff0c05b9cc2407cb38724df5be554738b180fabd96a730de0ceed327f8ba6cf4b78dbd0751f6270dd02b1087d2f7aa140010b8c0d22f8251f9b7b2e1e5df5e0521224b186a4c1f0776b62ca880f1c4447db22258b794aec2c5e6b5845a1bbe8b67c615465a6b56ef6da1830bd84f243cfe005010d247aee3d0d0dab3e33b317e2611d6e52df34bd5abf5a0cdd4937455832b1b0634b289ad4f96ea2946550c7dc94808dcfbdfad982f26827008c398b2e613ff728010e2162d1f86a6fe71314353527f64ba7397685bdc789cc7ebd50680625834ad2c554e5c57c26561774d528419a710b40f3a030fc46ac70f1dd79a39200a64215c3010f352d1943fea3821e3d07f53843ea7dc650d65a9eda692a26d5b8c691c3b7f1c44b4d3d1570525de2fdda4960c9fec5779b5cf6640dab19b13cac5477dcf3529b0112a0d821a4592cd3f4d8f071547efb6e6bb2500f4c8ef751943ab64c72c7a86bee38c82caad5d30edf9d981a21614e5cb8a1032e9a40f07a1db32345cb8b328c9a01648b5add00000000001af8cd23c2ab91237730770bbea08d61005cdda0984348f3f6eecb559638c0bba0000000001c5bd72f0150325748000300010001020005009d2efa1235ab86c0935cb424b102be4f217e74d1109df9e75dfa8338fc0f0908782f95862b045670cd22bee3114c39763a4a08beeb663b145d283c31d7d1101c4f000000057246017800000000019230f0fffffff8000000056e462864000000000182251c01000000170000001b00000000648b5add00000000648b5add00000000648b5adc000000057246017800000000019230f000000000648b5adc48d6033d733e27950c2e0351e2505491cd9154824f716d9513514c74b9f98f583dd2b63686a450ec7290df3a1e0b583c0481f651351edfa7636f39aed55cf8a3000000026a03186a0000000000b24171fffffff80000000268e6624c0000000000c34c3d010000001b0000002000000000648b5add00000000648b5add00000000648b5adc000000026a01e6920000000000b0546e00000000648b5adc3515b3861e8fe93e5f540ba4077c216404782b86d5e78077b3cbfd27313ab3bce62df6c8b4a85fe1a67db44dc12de5db330f7ac66b72dc658afedf0f4a415b430000024969e00abd000000007d0a84c3fffffff800000247c0dccf300000000070e4cda0010000001a0000001e00000000648b5add00000000648b5add00000000648b5adc000002496be5d2df000000007b04bca100000000648b5adc9b5f73e0075e7d70376012180ddba94272f68d85eae4104e335561c982253d41a19d04ac696c7a6616d291c7e5d1377cc8be437c327b75adb5dc1bad745fcae800000000039338e00000000000011170fffffff800000000038e9fbe00000000000129ed010000000f0000001100000000648b5add00000000648b5add00000000648b5adc00000000039338e0000000000001117000000000648b5adbe876fcd130add8984a33aab52af36bc1b9f822c9ebe376f3aa72d630974e15f0dcef50dd0a4cd2dcc17e45df1676dcb336a11a61c69df7a0299b0150c672d25c00000000005d3222000000000000133bfffffff800000000005cf05b000000000000153a010000001b0000002000000000648b5add00000000648b5add00000000648b5adc00000000005d3222000000000000133b00000000648b5adc", -]; - -export const testnet_valid_pfs = [ - "01000000000100888fa734558a251d97d73318de190a281dd2913c412e2656fc5c5a4023f5e2e26e8e4b7bf884880e5acc119f207248cdd62f65cca8f422b3f624a707af765ac701648b651c00000000001aa27839d641b07743c0cb5f68c51f8cd31d2c0762bec00dc6fcd25433ef1ab5b600000000052cf1670150325748000300010001020005009d1cdb1a5e1e3456d2977ee0d3d70765239f08a42855b9508fd479e15c6dc4d1feecf553770d9b10965f8fb64771e93f5690a182edc32be4a3236e0caaa6e0581a00000005810dc6000000000001a63b48fffffff80000000577e64610000000000185330c01000000010000000200000000648b651c00000000648b651c00000000648b651a00000005813a13cd000000000179ed7b00000000648b651a6a20671c0e3f8cb219ce3f46e5ae096a4f2fdf936d2bd4da8925f70087d51dd830029479598797290e3638a1712c29bde2367d0eca794f778b25b5a472f192de00000002719137720000000000da2f1ffffffff8000000026c540eac0000000000c2702501000000010000000200000000648b651c00000000648b651c00000000648b651a000000027192eddb0000000000d878b600000000648b651a28fe05d2708c6571182a7c9d1ff457a221b465edf5ea9af1373f9562d16b8d15f9c0172ba10dfa4d19088d94f5bf61d3b54d5bd7483a322a982e1373ee8ea31b0000024f477ea9b1000000005e977a96fffffff80000024b91f168800000000064b5bb4801000000010000000200000000648b651c00000000648b651c00000000648b651a0000024f4dc86f84000000006418cbfc00000000648b651a8b38db700e8b34640e681ec9a73e89608bda29415547a224f96585192b4b9dc794bce4aee88fdfa5b58d81090bd6b3784717fa6df85419d9f04433bb3d615d5c0000000003aaffab000000000001698cfffffff800000000039cca0c00000000000130a901000000010000000200000000648b651c00000000648b651c00000000648b651a0000000003ab5b030000000000013f8e00000000648b651a3b69a3cf075646c5fd8148b705b8107e61a1a253d5d8a84355dcb628b3f1d12031775e1d6897129e8a84eeba975778fb50015b88039e9bc140bbd839694ac0ae00000000005dfcd10000000000001343fffffff800000000005d7613000000000000151301000000010000000200000000648b651c00000000648b651c00000000648b651a00000000005dfd96000000000000170400000000648b651a", - "01000000000100a55ed74e8b21f57ea14988bb7b5afd51c2c7bf91c2987e0d03b9c48cb1e0473c6e019e7ae2a665270d4a2a00dab47a7abed19e94b832989215791b7eba8f289e00648b64f800000000001aa27839d641b07743c0cb5f68c51f8cd31d2c0762bec00dc6fcd25433ef1ab5b600000000052cee960150325748000300010001020005009d1cdb1a5e1e3456d2977ee0d3d70765239f08a42855b9508fd479e15c6dc4d1feecf553770d9b10965f8fb64771e93f5690a182edc32be4a3236e0caaa6e0581a0000000580ba2e960000000001642c3ffffffff80000000577cbf1d4000000000185567801000000010000000200000000648b64f800000000648b64f800000000648b64f700000005807657b600000000019c41c500000000648b64f76a20671c0e3f8cb219ce3f46e5ae096a4f2fdf936d2bd4da8925f70087d51dd830029479598797290e3638a1712c29bde2367d0eca794f778b25b5a472f192de00000002713911600000000000af5fb3fffffff8000000026c451af00000000000c2889701000000010000000200000000648b64f800000000648b64f800000000648b64f7000000027140b2800000000000b71b0000000000648b64f728fe05d2708c6571182a7c9d1ff457a221b465edf5ea9af1373f9562d16b8d15f9c0172ba10dfa4d19088d94f5bf61d3b54d5bd7483a322a982e1373ee8ea31b0000024f0d240e4f00000000750e2531fffffff80000024b87f3bae00000000064b0efc801000000010000000200000000648b64f800000000648b64f800000000648b64f80000024f10f80e6000000000713a252000000000648b64f88b38db700e8b34640e681ec9a73e89608bda29415547a224f96585192b4b9dc794bce4aee88fdfa5b58d81090bd6b3784717fa6df85419d9f04433bb3d615d5c0000000003aa552e0000000000010600fffffff800000000039ca25d00000000000130be01000000010000000200000000648b64f800000000648b64f800000000648b64f70000000003aa552e000000000000e3c800000000648b64f73b69a3cf075646c5fd8148b705b8107e61a1a253d5d8a84355dcb628b3f1d12031775e1d6897129e8a84eeba975778fb50015b88039e9bc140bbd839694ac0ae00000000005df1380000000000001324fffffff800000000005d749b000000000000151501000000010000000200000000648b64f800000000648b64f800000000648b64f700000000005deee0000000000000119400000000648b64f7", - "010000000001006aca274ebd019f90267f120bcf72935309f69ea651362dee74e312fa9e19ccbc0d5eb3e60fe5847bfd5fb5fe4b2fbb3c4dfb20b37b91739c679096512ac9093d00648b64f000000000001aa27839d641b07743c0cb5f68c51f8cd31d2c0762bec00dc6fcd25433ef1ab5b600000000052cedda0150325748000300010001020005009d3ca26715facde96b90a2e1e223fa85342f48561da88b7d60379587f6454a7966c2703fcc925ad32b6256afc3ebad634970d1b1ffb3f4143e36b2d055b1dcd29b0000000003016d5e0000000000016ff6fffffff80000000002ef5d46000000000001777001000000010000000200000000648b64f000000000648b64ef00000000648b64ed0000000003016d5e0000000000016ff600000000648b64eaebcc2747d96e7f9f41d7b21f764db2263ff84353da58ecab5e4623bea6ce195919546a41867d2e2d5ff2c181524f71f618477244bb14fb28dd4c22885ba4d8e700000000084825a70000000000057b0afffffff80000000008368f3c000000000005725e01000000010000000200000000648b64f000000000648b64ef00000000648b64ed00000000084825a70000000000057b0a00000000648b64ea2bc389a755a022a5305bc6d13d7445483a3367e66d90ca468cb909f8270b16e13f545e3f4ec9fd8eb3b9d9d6071a1da361f6729fa1b93d1d1baca3379551d99e0000000000037573000000000000017bfffffff8000000000003766700000000000002e901000000010000000200000000648b64f000000000648b64ef00000000648b64ed0000000000037573000000000000017b00000000648b64ea5da18437b2798113769bd3dff735d8fc068445f58c24daf29d5a88ebd0a7c3f2334a4429edb95cd4a3455d5133bbb568f9013b3ecb68962c89ee99c8e24818ad0000000000f012260000000000013c19fffffff80000000000efe716000000000001387f01000000010000000200000000648b64f000000000648b64ef00000000648b64ed0000000000f012260000000000013c1900000000648b64eafad307e1a5982b429896f172b86dba53ca31d5b09d373d929c7ba31026f99ea122d0b475cfe70ef0ff58d08efc3d72a78c7949d2c9f00dd1c8f8074e5bbf99e700000000023b84970000000000089f06fffffff80000000002359f41000000000008c44201000000010000000200000000648b64f000000000648b64ef00000000648b64ee00000000023b84970000000000089f0600000000648b64ea", - "01000000000100300f986bbe61faa6dd2ba34d4c8e2b37b7bbf0e9f04ac95e95b5e5ffb59815ab11f9419bd561553557c488470e6da92db5d3018e1e94b8d319cd40b400b0b0a700648b650600000000001aa27839d641b07743c0cb5f68c51f8cd31d2c0762bec00dc6fcd25433ef1ab5b600000000052cefb20150325748000300010001020005009d1cdb1a5e1e3456d2977ee0d3d70765239f08a42855b9508fd479e15c6dc4d1feecf553770d9b10965f8fb64771e93f5690a182edc32be4a3236e0caaa6e0581a0000000580a1890500000000017cd1d0fffffff80000000577d5caec0000000001854a9801000000010000000200000000648b650600000000648b650600000000648b65050000000580a189050000000001886ee300000000648b65056a20671c0e3f8cb219ce3f46e5ae096a4f2fdf936d2bd4da8925f70087d51dd830029479598797290e3638a1712c29bde2367d0eca794f778b25b5a472f192de00000002711bce9e0000000000bd7a62fffffff8000000026c4aea0c0000000000c2747f01000000010000000200000000648b650600000000648b650600000000648b650500000002712b01440000000000b1e46a00000000648b650528fe05d2708c6571182a7c9d1ff457a221b465edf5ea9af1373f9562d16b8d15f9c0172ba10dfa4d19088d94f5bf61d3b54d5bd7483a322a982e1373ee8ea31b0000024f05c1aa0f000000007bd7f2f1fffffff80000024b8b2042b00000000064c1e62001000000010000000200000000648b650600000000648b650600000000648b65050000024f0414405f000000007c542fa100000000648b65058b38db700e8b34640e681ec9a73e89608bda29415547a224f96585192b4b9dc794bce4aee88fdfa5b58d81090bd6b3784717fa6df85419d9f04433bb3d615d5c0000000003aa4657000000000000e759fffffff800000000039cb22a00000000000130a801000000010000000200000000648b650600000000648b650600000000648b65050000000003aa3a68000000000001071100000000648b65053b69a3cf075646c5fd8148b705b8107e61a1a253d5d8a84355dcb628b3f1d12031775e1d6897129e8a84eeba975778fb50015b88039e9bc140bbd839694ac0ae00000000005defaf00000000000014adfffffff800000000005d7526000000000000151401000000010000000200000000648b650600000000648b650600000000648b650500000000005df138000000000000145000000000648b6505", - "010000000001004c05a8cdad079c8a66982bf65fb976618105163d581faa3d8bddfa964a496e466f1f0b41f003e692df5d0d14b6e60041161ecee7089f4b5f41558fbcc78abea901648b64fd00000000001aa27839d641b07743c0cb5f68c51f8cd31d2c0762bec00dc6fcd25433ef1ab5b600000000052ceeed0150325748000300010001020005009d3ca26715facde96b90a2e1e223fa85342f48561da88b7d60379587f6454a7966c2703fcc925ad32b6256afc3ebad634970d1b1ffb3f4143e36b2d055b1dcd29b0000000003018aa10000000000016641fffffff80000000002ef71a5000000000001776001000000010000000200000000648b64fd00000000648b64fd00000000648b64fb0000000003018aa1000000000001664100000000648b64f4ebcc2747d96e7f9f41d7b21f764db2263ff84353da58ecab5e4623bea6ce195919546a41867d2e2d5ff2c181524f71f618477244bb14fb28dd4c22885ba4d8e7000000000846b373000000000006ed3ffffffff80000000008369e65000000000005734a01000000010000000200000000648b64fd00000000648b64fd00000000648b64fb000000000846b373000000000006ed3f00000000648b64f42bc389a755a022a5305bc6d13d7445483a3367e66d90ca468cb909f8270b16e13f545e3f4ec9fd8eb3b9d9d6071a1da361f6729fa1b93d1d1baca3379551d99e0000000000037573000000000000017bfffffff8000000000003766500000000000002e601000000010000000200000000648b64fd00000000648b64fd00000000648b64fb0000000000037573000000000000017b00000000648b64f45da18437b2798113769bd3dff735d8fc068445f58c24daf29d5a88ebd0a7c3f2334a4429edb95cd4a3455d5133bbb568f9013b3ecb68962c89ee99c8e24818ad0000000000f025c300000000000176aefffffff80000000000efe74b000000000001389701000000010000000200000000648b64fd00000000648b64fc00000000648b64fb0000000000f025c300000000000176ae00000000648b64f4fad307e1a5982b429896f172b86dba53ca31d5b09d373d929c7ba31026f99ea122d0b475cfe70ef0ff58d08efc3d72a78c7949d2c9f00dd1c8f8074e5bbf99e700000000023b84970000000000089f06fffffff8000000000235a5bd000000000008c41901000000010000000200000000648b64fd00000000648b64fd00000000648b64fb00000000023b84970000000000089f0600000000648b64f4", - "01000000000100e6ddeb64ac14d79c059744144001515ca58d3d23b1bec423310d3760ea4e001a2f0e235b15da59a023c70a5625848d0d907eacc4e70bb18a69d1727b56553e2101648b651200000000001aa27839d641b07743c0cb5f68c51f8cd31d2c0762bec00dc6fcd25433ef1ab5b600000000052cf09d0150325748000300010001020005009d1cdb1a5e1e3456d2977ee0d3d70765239f08a42855b9508fd479e15c6dc4d1feecf553770d9b10965f8fb64771e93f5690a182edc32be4a3236e0caaa6e0581a00000005810dc60000000000019d75b3fffffff80000000577de660c0000000001852cce01000000010000000200000000648b651200000000648b651100000000648b651000000005810dc60000000000019d75b300000000648b65106a20671c0e3f8cb219ce3f46e5ae096a4f2fdf936d2bd4da8925f70087d51dd830029479598797290e3638a1712c29bde2367d0eca794f778b25b5a472f192de00000002718d874a0000000000c6e6cafffffff8000000026c4fa9480000000000c26ab601000000010000000200000000648b651200000000648b651100000000648b651000000002718d37da0000000000b4886800000000648b651028fe05d2708c6571182a7c9d1ff457a221b465edf5ea9af1373f9562d16b8d15f9c0172ba10dfa4d19088d94f5bf61d3b54d5bd7483a322a982e1373ee8ea31b0000024f40bf5076000000006556d3d1fffffff80000024b8e8343d00000000064bb8fe601000000010000000200000000648b651200000000648b651100000000648b65100000024f40bf5076000000006556d3d100000000648b65108b38db700e8b34640e681ec9a73e89608bda29415547a224f96585192b4b9dc794bce4aee88fdfa5b58d81090bd6b3784717fa6df85419d9f04433bb3d615d5c0000000003aa55c40000000000012f26fffffff800000000039cbe90000000000001309a01000000010000000200000000648b651200000000648b651100000000648b65100000000003aa55c40000000000012f2600000000648b65103b69a3cf075646c5fd8148b705b8107e61a1a253d5d8a84355dcb628b3f1d12031775e1d6897129e8a84eeba975778fb50015b88039e9bc140bbd839694ac0ae00000000005dfaea000000000000171efffffff800000000005d759e000000000000151301000000010000000200000000648b651200000000648b651100000000648b651000000000005dfc7400000000000013a000000000648b6510", - "01000000000100cd52d0c580cc7478f1b58eec17b6ba9b10a37f505531e987ecb4cfae04a7fdb8276d7b12fa542191a84dc5a60472b7f926450a089449e0777f0c136ba16d1e1301648b651100000000001aa27839d641b07743c0cb5f68c51f8cd31d2c0762bec00dc6fcd25433ef1ab5b600000000052cf0840150325748000300010001020005009d3ca26715facde96b90a2e1e223fa85342f48561da88b7d60379587f6454a7966c2703fcc925ad32b6256afc3ebad634970d1b1ffb3f4143e36b2d055b1dcd29b0000000003026d1300000000000185dafffffff80000000002ef8d48000000000001776401000000010000000200000000648b651100000000648b651000000000648b650e0000000003026d1300000000000185da00000000648b650cebcc2747d96e7f9f41d7b21f764db2263ff84353da58ecab5e4623bea6ce195919546a41867d2e2d5ff2c181524f71f618477244bb14fb28dd4c22885ba4d8e700000000084a21ec0000000000077750fffffff8000000000836b70e00000000000573ca01000000010000000200000000648b651100000000648b651000000000648b650e00000000084a21ec000000000007775000000000648b650c2bc389a755a022a5305bc6d13d7445483a3367e66d90ca468cb909f8270b16e13f545e3f4ec9fd8eb3b9d9d6071a1da361f6729fa1b93d1d1baca3379551d99e0000000000037573000000000000017bfffffff8000000000003766200000000000002e201000000010000000200000000648b651100000000648b651000000000648b650e0000000000037573000000000000017b00000000648b650c5da18437b2798113769bd3dff735d8fc068445f58c24daf29d5a88ebd0a7c3f2334a4429edb95cd4a3455d5133bbb568f9013b3ecb68962c89ee99c8e24818ad0000000000f025c300000000000176aefffffff80000000000efe7a000000000000138e601000000010000000200000000648b651100000000648b651000000000648b650e0000000000f025c300000000000176ae00000000648b650bfad307e1a5982b429896f172b86dba53ca31d5b09d373d929c7ba31026f99ea122d0b475cfe70ef0ff58d08efc3d72a78c7949d2c9f00dd1c8f8074e5bbf99e700000000023bbf30000000000008b28ffffffff8000000000235aedb000000000008c3e401000000010000000200000000648b651100000000648b651000000000648b650e00000000023baba80000000000089f0700000000648b650c", - "010000000001006ab585e921b81d01fb6528cf6734d95fd869706f59d287a6b743b5fdbf0ebdbd103b04e480132a024225241f496b40cc008daf9471cd668e0583cc906e77069f01648b651600000000001aa27839d641b07743c0cb5f68c51f8cd31d2c0762bec00dc6fcd25433ef1ab5b600000000052cf0f50150325748000300010001020005009d3ca26715facde96b90a2e1e223fa85342f48561da88b7d60379587f6454a7966c2703fcc925ad32b6256afc3ebad634970d1b1ffb3f4143e36b2d055b1dcd29b000000000302ad570000000000017c17fffffff80000000002ef9525000000000001776c01000000010000000200000000648b651600000000648b651500000000648b6514000000000302c4ec00000000000193a900000000648b6510ebcc2747d96e7f9f41d7b21f764db2263ff84353da58ecab5e4623bea6ce195919546a41867d2e2d5ff2c181524f71f618477244bb14fb28dd4c22885ba4d8e7000000000848e17100000000000636d5fffffff8000000000836bd64000000000005745601000000010000000200000000648b651600000000648b651500000000648b651400000000084a21ec000000000007775000000000648b65102bc389a755a022a5305bc6d13d7445483a3367e66d90ca468cb909f8270b16e13f545e3f4ec9fd8eb3b9d9d6071a1da361f6729fa1b93d1d1baca3379551d99e0000000000037707000000000000030ffffffff8000000000003766200000000000002e101000000010000000200000000648b651600000000648b651500000000648b65140000000000037573000000000000017b00000000648b65105da18437b2798113769bd3dff735d8fc068445f58c24daf29d5a88ebd0a7c3f2334a4429edb95cd4a3455d5133bbb568f9013b3ecb68962c89ee99c8e24818ad0000000000effb930000000000014c7efffffff80000000000efe7ac00000000000138f201000000010000000200000000648b651600000000648b651500000000648b65140000000000effeaa0000000000014f9500000000648b6510fad307e1a5982b429896f172b86dba53ca31d5b09d373d929c7ba31026f99ea122d0b475cfe70ef0ff58d08efc3d72a78c7949d2c9f00dd1c8f8074e5bbf99e700000000023bbf30000000000008b28ffffffff8000000000235b183000000000008c3dd01000000010000000200000000648b651600000000648b651500000000648b651400000000023bbf30000000000008b28f00000000648b6510", - "01000000000100970313d82d9bf3fcfdaf8ed0b38c45cec7bad7093624ef5e7651cabfff706bc469f69951334e8555dbbbed3b1c0ceb3e4e9a65ab6558bd3655249dc7ee74d60500648b652500000000001aa27839d641b07743c0cb5f68c51f8cd31d2c0762bec00dc6fcd25433ef1ab5b600000000052cf2220150325748000300010001020005009d1cdb1a5e1e3456d2977ee0d3d70765239f08a42855b9508fd479e15c6dc4d1feecf553770d9b10965f8fb64771e93f5690a182edc32be4a3236e0caaa6e0581a0000000580ea12ea000000000154e016fffffff80000000577ec9eac0000000001851c6c01000000010000000200000000648b652500000000648b652400000000648b65220000000580ea12ea000000000154e01600000000648b65246a20671c0e3f8cb219ce3f46e5ae096a4f2fdf936d2bd4da8925f70087d51dd830029479598797290e3638a1712c29bde2367d0eca794f778b25b5a472f192de00000002718d17970000000000cf0b8efffffff8000000026c5719f80000000000c27cea01000000010000000200000000648b652500000000648b652400000000648b652300000002718fc1fb0000000000e21d8500000000648b652428fe05d2708c6571182a7c9d1ff457a221b465edf5ea9af1373f9562d16b8d15f9c0172ba10dfa4d19088d94f5bf61d3b54d5bd7483a322a982e1373ee8ea31b0000024f23ee6d200000000077306ce7fffffff80000024b93e12a900000000064bfcfde01000000010000000200000000648b652500000000648b652400000000648b65230000024f23ee6d20000000007aaf1ef700000000648b65248b38db700e8b34640e681ec9a73e89608bda29415547a224f96585192b4b9dc794bce4aee88fdfa5b58d81090bd6b3784717fa6df85419d9f04433bb3d615d5c0000000003aaac0a0000000000014abefffffff800000000039cd2dd00000000000130b001000000010000000200000000648b652500000000648b652400000000648b65230000000003aac2380000000000012ec800000000648b65243b69a3cf075646c5fd8148b705b8107e61a1a253d5d8a84355dcb628b3f1d12031775e1d6897129e8a84eeba975778fb50015b88039e9bc140bbd839694ac0ae00000000005df7350000000000001794fffffff800000000005d7673000000000000151201000000010000000200000000648b652500000000648b652400000000648b652300000000005dfa98000000000000119400000000648b6523", -]; - -export const mainnet_valid_guardians_set_upgrades = [ - { - vaa: "010000000001007ac31b282c2aeeeb37f3385ee0de5f8e421d30b9e5ae8ba3d4375c1c77a86e77159bb697d9c456d6f8c02d22a94b1279b65b0d6a9957e7d3857423845ac758e300610ac1d2000000030001000000000000000000000000000000000000000000000000000000000000000400000000000005390000000000000000000000000000000000000000000000000000000000436f7265020000000000011358cc3ae5c097b213ce3c81979e1b9f9570746aa5ff6cb952589bde862c25ef4392132fb9d4a42157114de8460193bdf3a2fcf81f86a09765f4762fd1107a0086b32d7a0977926a205131d8731d39cbeb8c82b2fd82faed2711d59af0f2499d16e726f6b211b39756c042441be6d8650b69b54ebe715e234354ce5b4d348fb74b958e8966e2ec3dbd4958a7cdeb5f7389fa26941519f0863349c223b73a6ddee774a3bf913953d695260d88bc1aa25a4eee363ef0000ac0076727b35fbea2dac28fee5ccb0fea768eaf45ced136b9d9e24903464ae889f5c8a723fc14f93124b7c738843cbb89e864c862c38cddcccf95d2cc37a4dc036a8d232b48f62cdd4731412f4890da798f6896a3331f64b48c12d1d57fd9cbe7081171aa1be1d36cafe3867910f99c09e347899c19c38192b6e7387ccd768277c17dab1b7a5027c0b3cf178e21ad2e77ae06711549cfbb1f9c7a9d8096e85e1487f35515d02a92753504a8d75471b9f49edb6fbebc898f403e4773e95feb15e80c9a99c8348d", - keys: [ - "2a953a2e8b1052eb70c1d7b556b087deed598b55608396686c1c811b9796c763078687ce10459f4f25fb7a0fbf8727bb0fb51e00820e93a123f652ee843cf08d", - "2766db08820e311b22e109801ab8ea505b12e3df3d91ebc87c999ffb6929d1abb0ade987c74aa37db26eea4086ee738a2f34a5594edb8760da0eac5be356b731", - "54177ff4a8329520b76efd86f8bfce5c942554db16e673267dc1133b3f5e230b2d8cbf90fe274946045d4491de288d736680edc2ee9ee5b1b15416b0a34806c4", - "7fa3e98fcc2621337b217b61408a98facaabd25bad2b158438728ce863c14708cfcda1f3b50a16ca0211199079fb338d479a54546ec3c5f775af23a7d7f4fb24", - "0bdcbccc0297c2a4f92a7c39358c42f22a8ed700a78bd05c39c8b61aaf2338e825b6c0d26d1f2a2ae4129cd751201f73d7234c753bd0735212a5288b19748fd2", - "cfd90084be68de514fe14a7c281f492223f045566f859ea5c166d6e60bc650c23940909a8e96c2fbffbd15a598b4e6a5b5aa14c126bf58cc1a9e396fe7771965", - "8edf3f9d997357a0e2c916ee090392c3a645ebac4f6cd8f826d3ecc0173b33bf06b7c14e8002fc9a5d01af9824a5cb3778472cd477e0ab378091448bca6f0417", - "6200deeadd3b4e2cd0a168d42e720a3b88d3ce4769ff2808086e6fd36110fc05c591ccbf91368609dc3b21203e58c9883d4f580085469bef8d5bdcd26eb8ad69", - "d5225476d7849b362226952ffc561bab99832f3f8b99741f6d81bbeaffa8e7f6e54a85e5029a3b510707eaa9684df496e4b1268075ad0328693a30bf1b1e0033", - "d9fa78b5b958bea1929080b8ad96dc555d34b051a27aebf711eb1186b807b0448316d994606ac807121838d6c41a58f308bc6307acdf69491fa4b17282f3e66f", - "cc64af75ec2e2741fb9af9f6191cb9ee187d6d26af4d1e96d7bab47e6ec09be12d3192030dc4bbf54d1da319a7a2acfc7a9dd4c644af6646a4aaa02b1024bbab", - "b5943b6e284682ad2e011d6962d41febf86af2f5fc0c9c8f4b81358ff077f9c96ba0880eaf93541eae94b4fa41dba66dab7fb0201cc9af7c75681e5719b0c95f", - "0cfc9d5b5dcf702a1525f9d4ed1841e8eb8b34434cc82470dd35435f1dbdc73ffb51544b7500394eac9c7fa567868b495326075147a2d809ebbfd43273eeec91", - "0aa78894d894a15933969f5826347439e2c309f2049277a10066c9197840499498ad19ee3d1b291f932ec0890bbdafcec292c4f02a446670cd0084f997e25e2f", - "00f400e3fe40f64032485aad9240ead45a8e1fc83ec08c96db861c0eca155ac898df8673e778e3ccaae8a0f9e6af415fe40e99b0cbc88d7610e536b6041b07fb", - "604f384174c7ed3a0dc5f476569a978266a7943bd775449d1b8b27f4eb8beb99cdf095f9200a2dabb1bc5d68c3d96ea3d47f4d34499d59953669b6c8c093d578", - "4881345cbb299fa7c60ab2d16cb7fe7bf8d14675506ef6eb6037038b5b7092ea0a9e4d0b53ba3904edd99f86717d6ba81dffe44eb5b23c6fd22c91ab73c33021", - "ee3d4cc17633afe7e1794fcfd728e0643325e3d130eb1daa39c0c5cb05a200b43876117a182cabdcc3795632aa529473a0c8245f9e4f6e43e54c3f1da28bcb82", - "21f338444e96af31cf44958acf5764844efbddace3b823ed761c340c59ed2685d829818c83eebe8f00f783f1048a53515845536668a9e0c059ade7579a0f4204", - ] - }, - { - vaa: "01000000010d0012e6b39c6da90c5dfd3c228edbb78c7a4c97c488ff8a346d161a91db067e51d638c17216f368aa9bdf4836b8645a98018ca67d2fec87d769cabfdf2406bf790a0002ef42b288091a670ef3556596f4f47323717882881eaf38e03345078d07a156f312b785b64dae6e9a87e3d32872f59cb1931f728cecf511762981baf48303668f0103cef2616b84c4e511ff03329e0853f1bd7ee9ac5ba71d70a4d76108bddf94f69c2a8a84e4ee94065e8003c334e899184943634e12043d0dda78d93996da073d190104e76d166b9dac98f602107cc4b44ac82868faf00b63df7d24f177aa391e050902413b71046434e67c770b19aecdf7fce1d1435ea0be7262e3e4c18f50ddc8175c0105d9450e8216d741e0206a50f93b750a47e0a258b80eb8fed1314cc300b3d905092de25cd36d366097b7103ae2d184121329ba3aa2d7c6cc53273f11af14798110010687477c8deec89d36a23e7948feb074df95362fc8dcbd8ae910ac556a1dee1e755c56b9db5d710c940938ed79bc1895a3646523a58bc55f475a23435a373ecfdd0107fb06734864f79def4e192497362513171530daea81f07fbb9f698afe7e66c6d44db21323144f2657d4a5386a954bb94eef9f64148c33aef6e477eafa2c5c984c01088769e82216310d1827d9bd48645ec23e90de4ef8a8de99e2d351d1df318608566248d80cdc83bdcac382b3c30c670352be87f9069aab5037d0b747208eae9c650109e9796497ff9106d0d1c62e184d83716282870cef61a1ee13d6fc485b521adcce255c96f7d1bca8d8e7e7d454b65783a830bddc9d94092091a268d311ecd84c26010c468c9fb6d41026841ff9f8d7368fa309d4dbea3ea4bbd2feccf94a92cc8a20a226338a8e2126cd16f70eaf15b4fc9be2c3fa19def14e071956a605e9d1ac4162010e23fcb6bd445b7c25afb722250c1acbc061ed964ba9de1326609ae012acdfb96942b2a102a2de99ab96327859a34a2b49a767dbdb62e0a1fb26af60fe44fd496a00106bb0bac77ac68b347645f2fb1ad789ea9bd76fb9b2324f25ae06f97e65246f142df717f662e73948317182c62ce87d79c73def0dba12e5242dfc038382812cfe00126da03c5e56cb15aeeceadc1e17a45753ab4dc0ec7bf6a75ca03143ed4a294f6f61bc3f478a457833e43084ecd7c985bf2f55a55f168aac0e030fc49e845e497101626e9d9a5d9e343f00010000000000000000000000000000000000000000000000000000000000000004c1759167c43f501c2000000000000000000000000000000000000000000000000000000000436f7265020000000000021358cc3ae5c097b213ce3c81979e1b9f9570746aa5ff6cb952589bde862c25ef4392132fb9d4a42157114de8460193bdf3a2fcf81f86a09765f4762fd1107a0086b32d7a0977926a205131d8731d39cbeb8c82b2fd82faed2711d59af0f2499d16e726f6b211b39756c042441be6d8650b69b54ebe715e234354ce5b4d348fb74b958e8966e2ec3dbd4958a7cd66b9590e1c41e0b226937bf9217d1d67fd4e91f574a3bf913953d695260d88bc1aa25a4eee363ef0000ac0076727b35fbea2dac28fee5ccb0fea768eaf45ced136b9d9e24903464ae889f5c8a723fc14f93124b7c738843cbb89e864c862c38cddcccf95d2cc37a4dc036a8d232b48f62cdd4731412f4890da798f6896a3331f64b48c12d1d57fd9cbe7081171aa1be1d36cafe3867910f99c09e347899c19c38192b6e7387ccd768277c17dab1b7a5027c0b3cf178e21ad2e77ae06711549cfbb1f9c7a9d8096e85e1487f35515d02a92753504a8d75471b9f49edb6fbebc898f403e4773e95feb15e80c9a99c8348d", - keys: [ - "2a953a2e8b1052eb70c1d7b556b087deed598b55608396686c1c811b9796c763078687ce10459f4f25fb7a0fbf8727bb0fb51e00820e93a123f652ee843cf08d", - "2766db08820e311b22e109801ab8ea505b12e3df3d91ebc87c999ffb6929d1abb0ade987c74aa37db26eea4086ee738a2f34a5594edb8760da0eac5be356b731", - "54177ff4a8329520b76efd86f8bfce5c942554db16e673267dc1133b3f5e230b2d8cbf90fe274946045d4491de288d736680edc2ee9ee5b1b15416b0a34806c4", - "7fa3e98fcc2621337b217b61408a98facaabd25bad2b158438728ce863c14708cfcda1f3b50a16ca0211199079fb338d479a54546ec3c5f775af23a7d7f4fb24", - "0bdcbccc0297c2a4f92a7c39358c42f22a8ed700a78bd05c39c8b61aaf2338e825b6c0d26d1f2a2ae4129cd751201f73d7234c753bd0735212a5288b19748fd2", - "cfd90084be68de514fe14a7c281f492223f045566f859ea5c166d6e60bc650c23940909a8e96c2fbffbd15a598b4e6a5b5aa14c126bf58cc1a9e396fe7771965", - "8edf3f9d997357a0e2c916ee090392c3a645ebac4f6cd8f826d3ecc0173b33bf06b7c14e8002fc9a5d01af9824a5cb3778472cd477e0ab378091448bca6f0417", - /**/ "6200deeadd3b4e2cd0a168d42e720a3b88d3ce4769ff2808086e6fd36110fc05c591ccbf91368609dc3b21203e58c9883d4f580085469bef8d5bdcd26eb8ad69", - "d5225476d7849b362226952ffc561bab99832f3f8b99741f6d81bbeaffa8e7f6e54a85e5029a3b510707eaa9684df496e4b1268075ad0328693a30bf1b1e0033", - "d9fa78b5b958bea1929080b8ad96dc555d34b051a27aebf711eb1186b807b0448316d994606ac807121838d6c41a58f308bc6307acdf69491fa4b17282f3e66f", - "cc64af75ec2e2741fb9af9f6191cb9ee187d6d26af4d1e96d7bab47e6ec09be12d3192030dc4bbf54d1da319a7a2acfc7a9dd4c644af6646a4aaa02b1024bbab", - "b5943b6e284682ad2e011d6962d41febf86af2f5fc0c9c8f4b81358ff077f9c96ba0880eaf93541eae94b4fa41dba66dab7fb0201cc9af7c75681e5719b0c95f", - "0cfc9d5b5dcf702a1525f9d4ed1841e8eb8b34434cc82470dd35435f1dbdc73ffb51544b7500394eac9c7fa567868b495326075147a2d809ebbfd43273eeec91", - "0aa78894d894a15933969f5826347439e2c309f2049277a10066c9197840499498ad19ee3d1b291f932ec0890bbdafcec292c4f02a446670cd0084f997e25e2f", - "00f400e3fe40f64032485aad9240ead45a8e1fc83ec08c96db861c0eca155ac898df8673e778e3ccaae8a0f9e6af415fe40e99b0cbc88d7610e536b6041b07fb", - "604f384174c7ed3a0dc5f476569a978266a7943bd775449d1b8b27f4eb8beb99cdf095f9200a2dabb1bc5d68c3d96ea3d47f4d34499d59953669b6c8c093d578", - "4881345cbb299fa7c60ab2d16cb7fe7bf8d14675506ef6eb6037038b5b7092ea0a9e4d0b53ba3904edd99f86717d6ba81dffe44eb5b23c6fd22c91ab73c33021", - "ee3d4cc17633afe7e1794fcfd728e0643325e3d130eb1daa39c0c5cb05a200b43876117a182cabdcc3795632aa529473a0c8245f9e4f6e43e54c3f1da28bcb82", - "21f338444e96af31cf44958acf5764844efbddace3b823ed761c340c59ed2685d829818c83eebe8f00f783f1048a53515845536668a9e0c059ade7579a0f4204", - ] - }, - { - vaa: "01000000020d00ce45474d9e1b1e7790a2d210871e195db53a70ffd6f237cfe70e2686a32859ac43c84a332267a8ef66f59719cf91cc8df0101fd7c36aa1878d5139241660edc0010375cc906156ae530786661c0cd9aef444747bc3d8d5aa84cac6a6d2933d4e1a031cffa30383d4af8131e929d9f203f460b07309a647d6cd32ab1cc7724089392c000452305156cfc90343128f97e499311b5cae174f488ff22fbc09591991a0a73d8e6af3afb8a5968441d3ab8437836407481739e9850ad5c95e6acfcc871e951bc30105a7956eefc23e7c945a1966d5ddbe9e4be376c2f54e45e3d5da88c2f8692510c7429b1ea860ae94d929bd97e84923a18187e777aa3db419813a80deb84cc8d22b00061b2a4f3d2666608e0aa96737689e3ba5793810ff3a52ff28ad57d8efb20967735dc5537a2e43ef10f583d144c12a1606542c207f5b79af08c38656d3ac40713301086b62c8e130af3411b3c0d91b5b50dcb01ed5f293963f901fc36e7b0e50114dce203373b32eb45971cef8288e5d928d0ed51cd86e2a3006b0af6a65c396c009080009e93ab4d2c8228901a5f4525934000b2c26d1dc679a05e47fdf0ff3231d98fbc207103159ff4116df2832eea69b38275283434e6cd4a4af04d25fa7a82990b707010aa643f4cf615dfff06ffd65830f7f6cf6512dabc3690d5d9e210fdc712842dc2708b8b2c22e224c99280cd25e5e8bfb40e3d1c55b8c41774e287c1e2c352aecfc010b89c1e85faa20a30601964ccc6a79c0ae53cfd26fb10863db37783428cd91390a163346558239db3cd9d420cfe423a0df84c84399790e2e308011b4b63e6b8015010ca31dcb564ac81a053a268d8090e72097f94f366711d0c5d13815af1ec7d47e662e2d1bde22678113d15963da100b668ba26c0c325970d07114b83c5698f46097010dc9fda39c0d592d9ed92cd22b5425cc6b37430e236f02d0d1f8a2ef45a00bde26223c0a6eb363c8b25fd3bf57234a1d9364976cefb8360e755a267cbbb674b39501108db01e444ab1003dd8b6c96f8eb77958b40ba7a85fefecf32ad00b7a47c0ae7524216262495977e09c0989dd50f280c21453d3756843608eacd17f4fdfe47600001261025228ef5af837cb060bcd986fcfa84ccef75b3fa100468cfd24e7fadf99163938f3b841a33496c2706d0208faab088bd155b2e20fd74c625bb1cc8c43677a0163c53c409e0c5dfa000100000000000000000000000000000000000000000000000000000000000000046c5a054d7833d1e42000000000000000000000000000000000000000000000000000000000436f7265020000000000031358cc3ae5c097b213ce3c81979e1b9f9570746aa5ff6cb952589bde862c25ef4392132fb9d4a42157114de8460193bdf3a2fcf81f86a09765f4762fd1107a0086b32d7a0977926a205131d8731d39cbeb8c82b2fd82faed2711d59af0f2499d16e726f6b211b39756c042441be6d8650b69b54ebe715e234354ce5b4d348fb74b958e8966e2ec3dbd4958a7cd15e7caf07c4e3dc8e7c469f92c8cd88fb8005a2074a3bf913953d695260d88bc1aa25a4eee363ef0000ac0076727b35fbea2dac28fee5ccb0fea768eaf45ced136b9d9e24903464ae889f5c8a723fc14f93124b7c738843cbb89e864c862c38cddcccf95d2cc37a4dc036a8d232b48f62cdd4731412f4890da798f6896a3331f64b48c12d1d57fd9cbe7081171aa1be1d36cafe3867910f99c09e347899c19c38192b6e7387ccd768277c17dab1b7a5027c0b3cf178e21ad2e77ae06711549cfbb1f9c7a9d8096e85e1487f35515d02a92753504a8d75471b9f49edb6fbebc898f403e4773e95feb15e80c9a99c8348d", - keys: [ - "2a953a2e8b1052eb70c1d7b556b087deed598b55608396686c1c811b9796c763078687ce10459f4f25fb7a0fbf8727bb0fb51e00820e93a123f652ee843cf08d", - "2766db08820e311b22e109801ab8ea505b12e3df3d91ebc87c999ffb6929d1abb0ade987c74aa37db26eea4086ee738a2f34a5594edb8760da0eac5be356b731", - "54177ff4a8329520b76efd86f8bfce5c942554db16e673267dc1133b3f5e230b2d8cbf90fe274946045d4491de288d736680edc2ee9ee5b1b15416b0a34806c4", - "7fa3e98fcc2621337b217b61408a98facaabd25bad2b158438728ce863c14708cfcda1f3b50a16ca0211199079fb338d479a54546ec3c5f775af23a7d7f4fb24", - "0bdcbccc0297c2a4f92a7c39358c42f22a8ed700a78bd05c39c8b61aaf2338e825b6c0d26d1f2a2ae4129cd751201f73d7234c753bd0735212a5288b19748fd2", - "cfd90084be68de514fe14a7c281f492223f045566f859ea5c166d6e60bc650c23940909a8e96c2fbffbd15a598b4e6a5b5aa14c126bf58cc1a9e396fe7771965", - "8edf3f9d997357a0e2c916ee090392c3a645ebac4f6cd8f826d3ecc0173b33bf06b7c14e8002fc9a5d01af9824a5cb3778472cd477e0ab378091448bca6f0417", - /**/ "6200deeadd3b4e2cd0a168d42e720a3b88d3ce4769ff2808086e6fd36110fc05c591ccbf91368609dc3b21203e58c9883d4f580085469bef8d5bdcd26eb8ad69", - "d5225476d7849b362226952ffc561bab99832f3f8b99741f6d81bbeaffa8e7f6e54a85e5029a3b510707eaa9684df496e4b1268075ad0328693a30bf1b1e0033", - "d9fa78b5b958bea1929080b8ad96dc555d34b051a27aebf711eb1186b807b0448316d994606ac807121838d6c41a58f308bc6307acdf69491fa4b17282f3e66f", - "cc64af75ec2e2741fb9af9f6191cb9ee187d6d26af4d1e96d7bab47e6ec09be12d3192030dc4bbf54d1da319a7a2acfc7a9dd4c644af6646a4aaa02b1024bbab", - "b5943b6e284682ad2e011d6962d41febf86af2f5fc0c9c8f4b81358ff077f9c96ba0880eaf93541eae94b4fa41dba66dab7fb0201cc9af7c75681e5719b0c95f", - "0cfc9d5b5dcf702a1525f9d4ed1841e8eb8b34434cc82470dd35435f1dbdc73ffb51544b7500394eac9c7fa567868b495326075147a2d809ebbfd43273eeec91", - "0aa78894d894a15933969f5826347439e2c309f2049277a10066c9197840499498ad19ee3d1b291f932ec0890bbdafcec292c4f02a446670cd0084f997e25e2f", - "00f400e3fe40f64032485aad9240ead45a8e1fc83ec08c96db861c0eca155ac898df8673e778e3ccaae8a0f9e6af415fe40e99b0cbc88d7610e536b6041b07fb", - "604f384174c7ed3a0dc5f476569a978266a7943bd775449d1b8b27f4eb8beb99cdf095f9200a2dabb1bc5d68c3d96ea3d47f4d34499d59953669b6c8c093d578", - "4881345cbb299fa7c60ab2d16cb7fe7bf8d14675506ef6eb6037038b5b7092ea0a9e4d0b53ba3904edd99f86717d6ba81dffe44eb5b23c6fd22c91ab73c33021", - "ee3d4cc17633afe7e1794fcfd728e0643325e3d130eb1daa39c0c5cb05a200b43876117a182cabdcc3795632aa529473a0c8245f9e4f6e43e54c3f1da28bcb82", - "21f338444e96af31cf44958acf5764844efbddace3b823ed761c340c59ed2685d829818c83eebe8f00f783f1048a53515845536668a9e0c059ade7579a0f4204", - ] - }, -]; diff --git a/tests/helpers.ts b/tests/helpers.ts deleted file mode 100644 index 2194e85..0000000 --- a/tests/helpers.ts +++ /dev/null @@ -1,56 +0,0 @@ -import { Clarinet, Tx, Chain, Account, types } from "https://deno.land/x/clarinet@v1.6.0/index.ts"; -import { assertEquals } from "https://deno.land/std@0.170.0/testing/asserts.ts"; -import { hexToBuffer } from "https://deno.land/x/hextools@v1.0.0/mod.ts"; -import { mainnet_valid_guardians_set_upgrades } from "./constants.ts"; - -export const executeGuardiansRotations = (chain: Chain, account: Account) => { - const vaaRotation1 = hexToBuffer(mainnet_valid_guardians_set_upgrades[0].vaa); - let publicKeysRotation1 = []; - for (let key of mainnet_valid_guardians_set_upgrades[0].keys) { - publicKeysRotation1.push(types.buff(hexToBuffer(key))); - } - - const vaaRotation2 = hexToBuffer(mainnet_valid_guardians_set_upgrades[1].vaa); - let publicKeysRotation2 = []; - for (let key of mainnet_valid_guardians_set_upgrades[1].keys) { - publicKeysRotation2.push(types.buff(hexToBuffer(key))); - } - - const vaaRotation3 = hexToBuffer(mainnet_valid_guardians_set_upgrades[2].vaa); - let publicKeysRotation3 = []; - for (let key of mainnet_valid_guardians_set_upgrades[2].keys) { - publicKeysRotation3.push(types.buff(hexToBuffer(key))); - } - - let block = chain.mineBlock([ - Tx.contractCall( - "wormhole-core-dev-preview-1", - "update-guardians-set", - [types.buff(vaaRotation1), types.list(publicKeysRotation1)], - account.address - ), - Tx.contractCall( - "wormhole-core-dev-preview-1", - "update-guardians-set", - [types.buff(vaaRotation2), types.list(publicKeysRotation2)], - account.address - ), - Tx.contractCall( - "wormhole-core-dev-preview-1", - "update-guardians-set", - [types.buff(vaaRotation3), types.list(publicKeysRotation3)], - account.address - ), - ]); - - // assert: review returned data, contract state, and other requirements - assertEquals(block.receipts.length, 3); - const rotation1 = block.receipts[0].result; - rotation1.expectOk(); - - const rotation2 = block.receipts[1].result; - rotation2.expectOk(); - - const rotation3 = block.receipts[2].result; - rotation3.expectOk(); -}; diff --git a/tests/pyth-oracle-v1_test.ts b/tests/pyth-oracle-v1_test.ts deleted file mode 100644 index 133c96d..0000000 --- a/tests/pyth-oracle-v1_test.ts +++ /dev/null @@ -1,61 +0,0 @@ -import { Clarinet, Tx, Chain, Account, types } from "https://deno.land/x/clarinet@v1.6.0/index.ts"; -import { - assertEquals, - assertObjectMatch, - assertArrayIncludes, -} from "https://deno.land/std@0.170.0/testing/asserts.ts"; -import { hexToBuffer } from "https://deno.land/x/hextools@v1.0.0/mod.ts"; - -import { mainnet_valid_pfs } from "./constants.ts"; -import { executeGuardiansRotations } from "./helpers.ts"; - -Clarinet.test({ - name: "Ensure that valid price attestations can be ingested and recorded", - fn(chain: Chain, accounts: Map) { - // arrange: set up the chain, state, and other required elements - const wallet_1 = accounts.get("wallet_1")!; - executeGuardiansRotations(chain, wallet_1); - - const vaaBytes = hexToBuffer(mainnet_valid_pfs[0]); - - const block = chain.mineBlock([ - Tx.contractCall( - "pyth-oracle-dev-preview-1", - "update-prices-feeds", - [types.list([types.buff(vaaBytes)])], - wallet_1.address - ), - ]); - - assertEquals(block.receipts.length, 1); - assertEquals(block.height, 3); - const priceUpdate = block.receipts[0].result; - // Ensure that the BTC_USD price feed id was the only updated feed - assertArrayIncludes(priceUpdate.expectOk().expectList(), [ - "0xe62df6c8b4a85fe1a67db44dc12de5db330f7ac66b72dc658afedf0f4a415b43", - ]); - - const feedIdBtcUsd = hexToBuffer( - "0xe62df6c8b4a85fe1a67db44dc12de5db330f7ac66b72dc658afedf0f4a415b43" - ); - const oracleResult = chain.callReadOnlyFn( - "pyth-oracle-dev-preview-1", - "read-price-feed", - [types.buff(feedIdBtcUsd)], - wallet_1.address - ); - assertObjectMatch(oracleResult.result.expectOk().expectTuple(), { - "attestation-time": "u1686854317", - conf: "u2064471426", - "ema-conf": "u1891952230", - "ema-price": "2507095440000", - expo: "4294967288", - "prev-conf": "u2064471426", - "prev-price": "2515455528574", - "prev-publish-time": "u1686854316", - price: "2515455528574", - "publish-time": "u1686854317", - status: "u1", - }); - }, -}); diff --git a/unit-tests/pyth-p2wh.test.ts b/unit-tests/pyth-p2wh.test.ts deleted file mode 100644 index f47c695..0000000 --- a/unit-tests/pyth-p2wh.test.ts +++ /dev/null @@ -1,66 +0,0 @@ -import { Cl, ClarityType, ResponseOkCV } from "@stacks/transactions"; -import { describe, expect, it } from "vitest"; -import { tx } from "@hirosystems/clarinet-sdk"; - -import { mainnet_valid_guardians_set_upgrades, mainnet_valid_pfs } from "./constants"; - -const pyth_oracle_v1_contract_name = "pyth-oracle-dev-preview-1"; -const wormhole_core_v1_contract_name = "wormhole-core-dev-preview-1"; - -describe("Pyth (P2WH) testsuite", () => { - const accounts = vm.getAccounts(); - const sender = accounts.get("wallet_1")!; - - it("ensure that legitimate price attestations are validated", () => { - const vaaRotation1 = Cl.bufferFromHex(mainnet_valid_guardians_set_upgrades[0].vaa); - let publicKeysRotation1 = mainnet_valid_guardians_set_upgrades[0].keys.map(Cl.bufferFromHex); - - const vaaRotation2 = Cl.bufferFromHex(mainnet_valid_guardians_set_upgrades[1].vaa); - let publicKeysRotation2 = mainnet_valid_guardians_set_upgrades[1].keys.map(Cl.bufferFromHex); - - const vaaRotation3 = Cl.bufferFromHex(mainnet_valid_guardians_set_upgrades[2].vaa); - let publicKeysRotation3 = mainnet_valid_guardians_set_upgrades[2].keys.map(Cl.bufferFromHex); - - const block1 = vm.mineBlock([ - tx.callPublicFn( - wormhole_core_v1_contract_name, - "update-guardians-set", - [vaaRotation1, Cl.list(publicKeysRotation1)], - sender - ), - tx.callPublicFn( - wormhole_core_v1_contract_name, - "update-guardians-set", - [vaaRotation2, Cl.list(publicKeysRotation2)], - sender - ), - tx.callPublicFn( - wormhole_core_v1_contract_name, - "update-guardians-set", - [vaaRotation3, Cl.list(publicKeysRotation3)], - sender - ), - ]); - - expect(block1).toHaveLength(3); - block1.forEach((b) => { - expect(b.result).toHaveClarityType(ClarityType.ResponseOk); - }); - - const vaaBytes = Cl.bufferFromHex(mainnet_valid_pfs[0]); - - let res = vm.callPublicFn( - pyth_oracle_v1_contract_name, - "update-prices-feeds", - [Cl.list([vaaBytes])], - sender - ); - - const result = res.result; - expect(result).toHaveClarityType(ClarityType.ResponseOk); - - expect((result as ResponseOkCV).value).toBeList([ - Cl.bufferFromHex("e62df6c8b4a85fe1a67db44dc12de5db330f7ac66b72dc658afedf0f4a415b43"), - ]); - }); -}); diff --git a/unit-tests/pyth-pnau.test.ts b/unit-tests/pyth-pnau.test.ts index 0d949c3..2d030f8 100644 --- a/unit-tests/pyth-pnau.test.ts +++ b/unit-tests/pyth-pnau.test.ts @@ -5,10 +5,12 @@ import { tx } from "@hirosystems/clarinet-sdk"; import { mainnet_valid_guardians_set_upgrades, mainnet_valid_au } from "./constants"; const pyth_oracle_v1_contract_name = "pyth-oracle-v1"; -const wormhole_core_v1_contract_name = "wormhole-core-dev-preview-1"; +const pyth_decoder_pnau_v1_contract_name = "pyth-pnau-decoder-v1"; +const pyth_storage_v1_contract_name = "pyth-store-v1"; +const wormhole_core_v1_contract_name = "wormhole-core-v1"; describe("Pyth (PNAU) testsuite", () => { - const accounts = vm.getAccounts(); + const accounts = simnet.getAccounts(); const sender = accounts.get("wallet_1")!; it("ensure that legitimate price attestations are validated", () => { @@ -21,7 +23,7 @@ describe("Pyth (PNAU) testsuite", () => { const vaaRotation3 = Cl.bufferFromHex(mainnet_valid_guardians_set_upgrades[2].vaa); let publicKeysRotation3 = mainnet_valid_guardians_set_upgrades[2].keys.map(Cl.bufferFromHex); - const block1 = vm.mineBlock([ + const block1 = simnet.mineBlock([ tx.callPublicFn( wormhole_core_v1_contract_name, "update-guardians-set", @@ -48,17 +50,22 @@ describe("Pyth (PNAU) testsuite", () => { }); const vaaBytes = Cl.bufferFromHex(mainnet_valid_au[0]); - - let res = vm.callPublicFn( + const executionPlan = Cl.tuple({ + 'pyth-storage-contract': Cl.contractPrincipal(simnet.deployer, pyth_storage_v1_contract_name), + 'pyth-decoder-contract': Cl.contractPrincipal(simnet.deployer, pyth_decoder_pnau_v1_contract_name), + 'wormhole-core-contract': Cl.contractPrincipal(simnet.deployer, wormhole_core_v1_contract_name), + }); + + let res = simnet.callPublicFn( pyth_oracle_v1_contract_name, - "update-prices-feeds", - [vaaBytes], + "verify-and-update-price-feeds", + [vaaBytes, executionPlan], sender ); // console.log(res.result); const result = res.result; - expect(result).toHaveClarityType(ClarityType.ResponseOk); + // expect(result).toHaveClarityType(ClarityType.ResponseOk); // expect(result as ResponseOkCV).toHaveClarityType(ClarityType.ResponseOk); console.log(Cl.prettyPrint(result, 2)); diff --git a/unit-tests/utils/cursor.test.ts b/unit-tests/utils/cursor.test.ts new file mode 100644 index 0000000..9fc4bf8 --- /dev/null +++ b/unit-tests/utils/cursor.test.ts @@ -0,0 +1,540 @@ +import { Cl } from "@stacks/transactions"; +import { describe, expect } from "vitest"; +// import { tx } from "@hirosystems/clarinet-sdk"; +import { it, fc } from '@fast-check/vitest'; +import { concatTypedArrays, uint8toBytes, uint16toBytes, uint32toBytes, bigintToBuffer } from './helper'; + +const cursor_contract_name = "hk-cursor-v1"; + +describe("hiro-kit::cursor - buffers", () => { + const accounts = simnet.getAccounts(); + const sender = accounts.get("wallet_1")!; + + const buff_n = (n: number) => { + return fc.uint8Array({ minLength: 0, maxLength: n }) + } + + const bytesToRead = () => { + return fc.constantFrom(1, 2, 4, 8, 16, 20, 64, 65) + } + + const bytesToGenerate = (n: number) => { + return fc.uint8Array({ minLength: 0, maxLength: n }) + } + + it.prop([buff_n(8192)])("read-buff-1", (numbers) => { + var data = new Uint8Array(); + for (let n of numbers) { + data = concatTypedArrays(data, uint8toBytes(n)); + } + let nextInput = Cl.tuple({ + bytes: Cl.buffer(data), + pos: Cl.uint(0) + }) + + let pos = 0; + for (let n of numbers) { + pos += 1; + let res = simnet.callReadOnlyFn( + cursor_contract_name, + "read-buff-1", + [nextInput], + sender + ); + nextInput = Cl.tuple({ + bytes: Cl.buffer(data), + pos: Cl.uint(pos) + }); + expect(res.result).toBeOk(Cl.tuple({ + value: Cl.buffer(uint8toBytes(n)), + next: nextInput + })) + } + }) + + it.prop([fc.uniqueArray(bytesToRead())])("read-buff-n", (buffer) => { + var data = new Uint8Array(); + let segments = []; + for (let value of buffer) { + var segment = new Uint8Array(); + for (let i = 0; i < value; i++) { + segment = concatTypedArrays(segment, uint8toBytes(value)); + } + segments.push(segment); + data = concatTypedArrays(data, segment); + } + + let nextInput = Cl.tuple({ + bytes: Cl.buffer(data), + pos: Cl.uint(0) + }) + + let pos = 0; + for (let segment of segments) { + let len = segment.length; + pos += len; + let res = simnet.callReadOnlyFn( + cursor_contract_name, + `read-buff-${len}`, + [nextInput], + sender + ); + nextInput = Cl.tuple({ + bytes: Cl.buffer(data), + pos: Cl.uint(pos) + }); + expect(res.result).toBeOk(Cl.tuple({ + value: Cl.buffer(segment), + next: nextInput + })) + } + }) + + it.prop([bytesToRead(), bytesToGenerate(8192)])("read-buff-8192-max and slice", (toRead, toGenerate) => { + var data = new Uint8Array(); + for (let n of toGenerate) { + data = concatTypedArrays(data, uint8toBytes(n)); + } + let input = Cl.tuple({ + bytes: Cl.buffer(data), + pos: Cl.uint(0) + }) + + let res = simnet.callReadOnlyFn( + cursor_contract_name, + `read-buff-${toRead}`, + [input], + sender + ); + + // Early return: we're tryint to read more byte than the len of the buffer + if (toRead > data.length) { + expect(res.result).toBeErr(Cl.uint(1)) + return; + } + + let expectedCursor = Cl.tuple({ + bytes: Cl.buffer(data), + pos: Cl.uint(toRead) + }); + + expect(res.result).toBeOk(Cl.tuple({ + value: Cl.buffer(data.subarray(0, toRead)), + next: expectedCursor + })) + + res = simnet.callReadOnlyFn( + cursor_contract_name, + `read-buff-8192-max`, + [expectedCursor, Cl.none()], + sender + ); + + // Early return: we're tryint to read more byte than the len of the buffer + if (toRead == data.length) { + expect(res.result).toBeErr(Cl.uint(1)) + return; + } + + let finalCursor = Cl.tuple({ + bytes: Cl.buffer(data), + pos: Cl.uint(data.length) + }); + + let expectedBuffer = Cl.buffer(data.subarray(toRead, data.length)); + + expect(res.result).toBeOk(Cl.tuple({ + value: expectedBuffer, + next: finalCursor + })) + + res = simnet.callReadOnlyFn( + cursor_contract_name, + `slice`, + [expectedCursor, Cl.none()], + sender + ); + expect(Cl.ok(res.result)).toBeOk(expectedBuffer); + }) + + it.prop([bytesToRead(), bytesToRead(), bytesToGenerate(8192)])("read-buff-8192-max with limit and slice", (toRead, toIsolate, toGenerate) => { + var data = new Uint8Array(); + for (let n of toGenerate) { + data = concatTypedArrays(data, uint8toBytes(n)); + } + let input = Cl.tuple({ + bytes: Cl.buffer(data), + pos: Cl.uint(0) + }) + + let res = simnet.callReadOnlyFn( + cursor_contract_name, + `read-buff-${toRead}`, + [input], + sender + ); + + // Early return: we're tryint to read more byte than the len of the buffer + if (toRead > data.length) { + expect(res.result).toBeErr(Cl.uint(1)) + return; + } + + let expectedCursor = Cl.tuple({ + bytes: Cl.buffer(data), + pos: Cl.uint(toRead) + }); + + expect(res.result).toBeOk(Cl.tuple({ + value: Cl.buffer(data.subarray(0, toRead)), + next: expectedCursor + })) + + res = simnet.callReadOnlyFn( + cursor_contract_name, + `read-buff-8192-max`, + [expectedCursor, Cl.some(Cl.uint(toIsolate))], + sender + ); + + // Early return: we're tryint to read more byte than the len of the buffer + if (toRead + toIsolate > data.length) { + expect(res.result).toBeErr(Cl.uint(1)) + return; + } + + let finalCursor = Cl.tuple({ + bytes: Cl.buffer(data), + pos: Cl.uint(toRead + toIsolate) + }); + + let expectedBuffer = Cl.buffer(data.subarray(toRead, toRead + toIsolate)); + + expect(res.result).toBeOk(Cl.tuple({ + value: expectedBuffer, + next: finalCursor + })) + }) + +}) + +describe("hiro-kit::cursor - unsigned integers", () => { + const accounts = simnet.getAccounts(); + const sender = accounts.get("wallet_1")!; + + let arrayOfUint8 = fc.uint8Array({ minLength: 0, maxLength: 8192 }); + it.prop([arrayOfUint8])("read-uint-8", (numbers) => { + var data = new Uint8Array(); + for (let n of numbers) { + data = concatTypedArrays(data, uint8toBytes(n)); + } + let nextInput = Cl.tuple({ + bytes: Cl.buffer(data), + pos: Cl.uint(0) + }) + + let pos = 0; + for (let n of numbers) { + pos += 1; + let res = simnet.callReadOnlyFn( + cursor_contract_name, + "read-uint-8", + [nextInput], + sender + ); + nextInput = Cl.tuple({ + bytes: Cl.buffer(data), + pos: Cl.uint(pos) + }); + expect(res.result).toBeOk(Cl.tuple({ + value: Cl.uint(n), + next: nextInput + })) + } + }) + + let arrayOfUint16 = fc.uint16Array({ minLength: 0, maxLength: 4196 }); + it.prop([arrayOfUint16])("read-uint-16", (numbers) => { + var data = new Uint8Array(); + for (let n of numbers) { + data = concatTypedArrays(data, uint16toBytes(n)); + } + let nextInput = Cl.tuple({ + bytes: Cl.buffer(data), + pos: Cl.uint(0) + }) + + let pos = 0; + for (let n of numbers) { + pos += 2; + let res = simnet.callReadOnlyFn( + cursor_contract_name, + "read-uint-16", + [nextInput], + sender + ); + nextInput = Cl.tuple({ + bytes: Cl.buffer(data), + pos: Cl.uint(pos) + }); + expect(res.result).toBeOk(Cl.tuple({ + value: Cl.uint(n), + next: nextInput + })) + } + }) + + let arrayOfUint32 = fc.uint32Array({ minLength: 0, maxLength: 2048 }); + it.prop([arrayOfUint32])("read-uint-32", (numbers) => { + var data = new Uint8Array(); + + for (let n of numbers) { + data = concatTypedArrays(data, uint32toBytes(n)); + } + let nextInput = Cl.tuple({ + bytes: Cl.buffer(data), + pos: Cl.uint(0) + }) + + let pos = 0; + for (let n of numbers) { + pos += 4; + let res = simnet.callReadOnlyFn( + cursor_contract_name, + "read-uint-32", + [nextInput], + sender + ); + nextInput = Cl.tuple({ + bytes: Cl.buffer(data), + pos: Cl.uint(pos) + }); + expect(res.result).toBeOk(Cl.tuple({ + value: Cl.uint(n), + next: nextInput + })) + } + }) + + let arrayOfUint64 = fc.array(fc.bigUintN(64), { minLength: 0, maxLength: 1024 }); + it.prop([arrayOfUint64])("read-uint-64", (numbers) => { + var data = Buffer.from([]); + for (let n of numbers) { + data = Buffer.concat([data, Buffer.from(bigintToBuffer(n, 8))]); + } + let nextInput = Cl.tuple({ + bytes: Cl.buffer(data), + pos: Cl.uint(0) + }) + + let pos = 0; + for (let n of numbers) { + pos += 8; + let res = simnet.callReadOnlyFn( + cursor_contract_name, + "read-uint-64", + [nextInput], + sender + ); + nextInput = Cl.tuple({ + bytes: Cl.buffer(data), + pos: Cl.uint(pos) + }); + expect(res.result).toBeOk(Cl.tuple({ + value: Cl.uint(n), + next: nextInput + })) + } + }) + + let arrayOfUint128 = fc.array(fc.bigUintN(128), { minLength: 0, maxLength: 512 }); + it.prop([arrayOfUint128])("read-uint-128", (numbers) => { + var data = Buffer.from([]); + for (let n of numbers) { + data = Buffer.concat([data, Buffer.from(bigintToBuffer(n, 16))]); + } + let nextInput = Cl.tuple({ + bytes: Cl.buffer(data), + pos: Cl.uint(0) + }) + + let pos = 0; + for (let n of numbers) { + pos += 16; + let res = simnet.callReadOnlyFn( + cursor_contract_name, + "read-uint-128", + [nextInput], + sender + ); + nextInput = Cl.tuple({ + bytes: Cl.buffer(data), + pos: Cl.uint(pos) + }); + expect(res.result).toBeOk(Cl.tuple({ + value: Cl.uint(n), + next: nextInput + })) + } + }) +}) + +describe("hiro-kit::cursor - signed integers", () => { + const accounts = simnet.getAccounts(); + const sender = accounts.get("wallet_1")!; + + let arrayOfInt8 = fc.array(fc.bigIntN(8), { minLength: 0, maxLength: 1023 }); + it.prop([arrayOfInt8])("read-int-8", (numbers) => { + var data = Buffer.from([]); + for (let n of numbers) { + data = Buffer.concat([data, Buffer.from(bigintToBuffer(n, 1))]); + } + let nextInput = Cl.tuple({ + bytes: Cl.buffer(data), + pos: Cl.uint(0) + }) + + let pos = 0; + for (let n of numbers) { + pos += 1; + let res = simnet.callReadOnlyFn( + cursor_contract_name, + "read-int-8", + [nextInput], + sender + ); + nextInput = Cl.tuple({ + bytes: Cl.buffer(data), + pos: Cl.uint(pos) + }); + expect(res.result).toBeOk(Cl.tuple({ + value: Cl.int(n), + next: nextInput + })) + } + }) + + let arrayOfInt16 = fc.array(fc.bigIntN(16), { minLength: 0, maxLength: 1023 }); + it.prop([arrayOfInt16])("read-int-16", (numbers) => { + var data = Buffer.from([]); + for (let n of numbers) { + data = Buffer.concat([data, Buffer.from(bigintToBuffer(n, 2))]); + } + let nextInput = Cl.tuple({ + bytes: Cl.buffer(data), + pos: Cl.uint(0) + }) + + let pos = 0; + for (let n of numbers) { + pos += 2; + let res = simnet.callReadOnlyFn( + cursor_contract_name, + "read-int-16", + [nextInput], + sender + ); + nextInput = Cl.tuple({ + bytes: Cl.buffer(data), + pos: Cl.uint(pos) + }); + expect(res.result).toBeOk(Cl.tuple({ + value: Cl.int(n), + next: nextInput + })) + } + }) + + let arrayOfInt32 = fc.array(fc.bigIntN(32), { minLength: 0, maxLength: 1023 }); + it.prop([arrayOfInt32])("read-int-32", (numbers) => { + var data = Buffer.from([]); + for (let n of numbers) { + data = Buffer.concat([data, Buffer.from(bigintToBuffer(n, 4))]); + } + let nextInput = Cl.tuple({ + bytes: Cl.buffer(data), + pos: Cl.uint(0) + }) + + let pos = 0; + for (let n of numbers) { + pos += 4; + let res = simnet.callReadOnlyFn( + cursor_contract_name, + "read-int-32", + [nextInput], + sender + ); + nextInput = Cl.tuple({ + bytes: Cl.buffer(data), + pos: Cl.uint(pos) + }); + expect(res.result).toBeOk(Cl.tuple({ + value: Cl.int(n), + next: nextInput + })) + } + }) + + let arrayOfInt64 = fc.array(fc.bigIntN(64), { minLength: 0, maxLength: 1023 }); + it.prop([arrayOfInt64])("read-int-64", (numbers) => { + var data = Buffer.from([]); + for (let n of numbers) { + data = Buffer.concat([data, Buffer.from(bigintToBuffer(n, 8))]); + } + let nextInput = Cl.tuple({ + bytes: Cl.buffer(data), + pos: Cl.uint(0) + }) + + let pos = 0; + for (let n of numbers) { + pos += 8; + let res = simnet.callReadOnlyFn( + cursor_contract_name, + "read-int-64", + [nextInput], + sender + ); + nextInput = Cl.tuple({ + bytes: Cl.buffer(data), + pos: Cl.uint(pos) + }); + expect(res.result).toBeOk(Cl.tuple({ + value: Cl.int(n), + next: nextInput + })) + } + }) + + let arrayOfInt128 = fc.array(fc.bigIntN(128), { minLength: 0, maxLength: 1023 }); + it.prop([arrayOfInt128])("read-int-128", (numbers) => { + var data = Buffer.from([]); + for (let n of numbers) { + data = Buffer.concat([data, Buffer.from(bigintToBuffer(n, 16))]); + } + let nextInput = Cl.tuple({ + bytes: Cl.buffer(data), + pos: Cl.uint(0) + }) + + let pos = 0; + for (let n of numbers) { + pos += 16; + let res = simnet.callReadOnlyFn( + cursor_contract_name, + "read-int-128", + [nextInput], + sender + ); + nextInput = Cl.tuple({ + bytes: Cl.buffer(data), + pos: Cl.uint(pos) + }); + expect(res.result).toBeOk(Cl.tuple({ + value: Cl.int(n), + next: nextInput + })) + } + }) +}) diff --git a/unit-tests/utils/helper.ts b/unit-tests/utils/helper.ts new file mode 100644 index 0000000..d760dc0 --- /dev/null +++ b/unit-tests/utils/helper.ts @@ -0,0 +1,42 @@ +export function concatTypedArrays(a: any, b: any) { + var c = new (a.constructor)(a.length + b.length); + c.set(a, 0); + c.set(b, a.length); + return c; +} + +export const uint8toBytes = (num: number) => { + let b = new ArrayBuffer(1); + new DataView(b).setUint8(0, num); + return new Uint8Array(b); +} + +export const uint16toBytes = (num: number) => { + let b = new ArrayBuffer(2); + new DataView(b).setUint16(0, num); + return new Uint8Array(b); +} + +export const uint32toBytes = (num: number) => { + let b = new ArrayBuffer(4); + new DataView(b).setUint32(0, num); + return new Uint8Array(b); +} + +export function bigintToBuffer(bigintValue: bigint, byteLength: number) { + if (bigintValue >= 0n) { + let hexString = bigintValue.toString(16); // Convert BigInt to hexadecimal string + const padding = byteLength * 2 - hexString.length; // Calculate padding + + // Add leading zeros for padding + for (let i = 0; i < padding; i++) { + hexString = '0' + hexString; + } + + return Buffer.from(hexString, 'hex'); // Create Buffer from padded hexadecimal string + } else { + // Handle negative BigInt + const twosComplement = (BigInt(1) << BigInt(byteLength * 8)) + bigintValue; + return bigintToBuffer(twosComplement, byteLength); + } +} diff --git a/unit-tests/utils/merkle-proofs.test.ts b/unit-tests/utils/merkle-proofs.test.ts new file mode 100644 index 0000000..3314b3c --- /dev/null +++ b/unit-tests/utils/merkle-proofs.test.ts @@ -0,0 +1,170 @@ +import { Cl } from "@stacks/transactions"; +import { describe, expect, it } from "vitest"; +// import { tx } from "@hirosystems/clarinet-sdk"; + +const merkle_contract_name = "hk-merkle-tree-keccak160-v1"; + +// The test vectors executed in the following test suite are coming from: +// https://github.com/pyth-network/pyth-crosschain/blob/main/target_chains/aptos/contracts/sources/merkle.move + +describe("hiro-kit::hk-merkle-tree-keccak160", () => { + const accounts = simnet.getAccounts(); + const sender = accounts.get("wallet_1")!; + + const setupTree = () => { + // + // h1 h2 h3 h4 + // \ / \ / + // h5 h6 + // \ / + // h7 + // + let h1 = simnet.callReadOnlyFn( + merkle_contract_name, + "hash-leaf", + [Cl.bufferFromHex("adad11")], + sender + ).result; + let h2 = simnet.callReadOnlyFn( + merkle_contract_name, + "hash-leaf", + [Cl.bufferFromHex("adad12")], + sender + ).result; + let h3 = simnet.callReadOnlyFn( + merkle_contract_name, + "hash-leaf", + [Cl.bufferFromHex("adad13")], + sender + ).result; + let h4 = simnet.callReadOnlyFn( + merkle_contract_name, + "hash-leaf", + [Cl.bufferFromHex("adad14")], + sender + ).result; + let h5 = simnet.callReadOnlyFn( + merkle_contract_name, + "hash-nodes", + [h1, h2], + sender + ).result; + let h6 = simnet.callReadOnlyFn( + merkle_contract_name, + "hash-nodes", + [h3, h4], + sender + ).result; + let h7 = simnet.callReadOnlyFn( + merkle_contract_name, + "hash-nodes", + [h5, h6], + sender + ).result; + return [h1, h2, h3, h4, h5, h6, h7] + } + + it("hash leaf", () => { + let res = simnet.callReadOnlyFn( + merkle_contract_name, + "hash-leaf", + [Cl.bufferFromHex("00640000000000000000000000000000000000000000000000000000000000000000000000000000640000000000000064000000640000000000000064000000000000006400000000000000640000000000000064")], + sender + ); + expect(res.result).toStrictEqual(Cl.bufferFromHex("afc6a8ac466430f35895055f8a4c951785dad5ce")) + }); + + it("hash node", () => { + let h1 = Cl.bufferFromHex("05c51b04b820c0f704e3fdd2e4fc1e70aff26dff"); + let h2 = Cl.bufferFromHex("1e108841c8d21c7a5c4860c8c3499c918ea9e0ac"); + let res = simnet.callReadOnlyFn( + merkle_contract_name, + "hash-nodes", + [h1, h2], + sender + ); + expect(res.result).toStrictEqual(Cl.bufferFromHex("2d0e4fde68184c7ce8af426a0865bd41ef84dfa4")) + }); + + it("check valid proofs", () => { + let [h1, h2, h3, h4, h5, h6, h7] = setupTree(); + + expect(simnet.callReadOnlyFn( + merkle_contract_name, + "check-proof", + [h7, Cl.bufferFromHex("adad11"), Cl.list([h2, h6])], + sender + ).result).toStrictEqual(Cl.bool(true)); + + expect(simnet.callReadOnlyFn( + merkle_contract_name, + "check-proof", + [h7, Cl.bufferFromHex("adad14"), Cl.list([h3, h5])], + sender + ).result).toStrictEqual(Cl.bool(true)); + + expect(simnet.callReadOnlyFn( + merkle_contract_name, + "check-proof", + [h7, Cl.bufferFromHex("adad14"), Cl.list([h1, h4])], + sender + ).result).toStrictEqual(Cl.bool(false)); + }); + + it("check valid proofs subtree", () => { + let [h1, h2, h3, h4, h5, h6, _] = setupTree(); + + expect(simnet.callReadOnlyFn( + merkle_contract_name, + "check-proof", + [h5, Cl.bufferFromHex("adad12"), Cl.list([h1])], + sender + ).result).toStrictEqual(Cl.bool(true)); + + expect(simnet.callReadOnlyFn( + merkle_contract_name, + "check-proof", + [h5, Cl.bufferFromHex("adad11"), Cl.list([h2])], + sender + ).result).toStrictEqual(Cl.bool(true)); + + expect(simnet.callReadOnlyFn( + merkle_contract_name, + "check-proof", + [h6, Cl.bufferFromHex("adad14"), Cl.list([h3])], + sender + ).result).toStrictEqual(Cl.bool(true)); + + expect(simnet.callReadOnlyFn( + merkle_contract_name, + "check-proof", + [h6, Cl.bufferFromHex("adad13"), Cl.list([h4])], + sender + ).result).toStrictEqual(Cl.bool(true)); + }); + + it("check invalid proofs", () => { + let [h1, h2, h3, h4, h5, h6, h7] = setupTree(); + + expect(simnet.callReadOnlyFn( + merkle_contract_name, + "check-proof", + [h7, Cl.bufferFromHex("dead"), Cl.list([h2, h6])], + sender + ).result).toStrictEqual(Cl.bool(false)); + + expect(simnet.callReadOnlyFn( + merkle_contract_name, + "check-proof", + [h7, Cl.bufferFromHex("dead"), Cl.list([h3, h5])], + sender + ).result).toStrictEqual(Cl.bool(false)); + + expect(simnet.callReadOnlyFn( + merkle_contract_name, + "check-proof", + [h7, Cl.bufferFromHex("dead"), Cl.list([h1, h4])], + sender + ).result).toStrictEqual(Cl.bool(false)); + }); +}) diff --git a/unit-tests/wormhole.test.ts b/unit-tests/wormhole.test.ts index 74a0040..c8d8143 100644 --- a/unit-tests/wormhole.test.ts +++ b/unit-tests/wormhole.test.ts @@ -4,10 +4,10 @@ import { tx } from "@hirosystems/clarinet-sdk"; import { mainnet_valid_guardians_set_upgrades } from "./constants"; -const wormhole_core_v1_contract_name = "wormhole-core-dev-preview-1"; +const wormhole_core_v1_contract_name = "wormhole-core-v1"; describe("Wormhole testsuite", () => { - const accounts = vm.getAccounts(); + const accounts = simnet.getAccounts(); const sender = accounts.get("wallet_1")!; it("ensure that guardians set can be rotated", () => { @@ -20,7 +20,7 @@ describe("Wormhole testsuite", () => { const vaaRotation3 = Cl.bufferFromHex(mainnet_valid_guardians_set_upgrades[2].vaa); let publicKeysRotation3 = mainnet_valid_guardians_set_upgrades[2].keys.map(Cl.bufferFromHex); - const block1 = vm.mineBlock([ + const block1 = simnet.mineBlock([ tx.callPublicFn( wormhole_core_v1_contract_name, "update-guardians-set",