Pyth.sol 6.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185
  1. // contracts/Bridge.sol
  2. // SPDX-License-Identifier: Apache 2
  3. pragma solidity ^0.8.0;
  4. import "../libraries/external/BytesLib.sol";
  5. import "./PythGetters.sol";
  6. import "./PythSetters.sol";
  7. import "./PythStructs.sol";
  8. import "./PythGovernance.sol";
  9. contract Pyth is PythGovernance {
  10. using BytesLib for bytes;
  11. function attestPrice(bytes memory encodedVm) public returns (PythStructs.PriceAttestation memory pa) {
  12. (IWormhole.VM memory vm, bool valid, string memory reason) = wormhole().parseAndVerifyVM(encodedVm);
  13. require(valid, reason);
  14. require(verifyPythVM(vm), "invalid emitter");
  15. PythStructs.PriceAttestation memory price = parsePriceAttestation(vm.payload);
  16. PythStructs.PriceAttestation memory latestPrice = latestAttestation(price.productId, price.priceType);
  17. if(price.timestamp > latestPrice.timestamp) {
  18. setLatestAttestation(price.productId, price.priceType, price);
  19. }
  20. return price;
  21. }
  22. function verifyPythVM(IWormhole.VM memory vm) public view returns (bool valid) {
  23. if (vm.emitterChainId != pyth2WormholeChainId()) {
  24. return false;
  25. }
  26. if (vm.emitterAddress != pyth2WormholeEmitter()) {
  27. return false;
  28. }
  29. return true;
  30. }
  31. function parseBatchPriceAttestation(bytes memory encoded) public pure returns (PythStructs.BatchPriceAttestation memory bpa) {
  32. uint index = 0;
  33. // Check header
  34. bpa.header.magic = encoded.toUint32(index);
  35. index += 4;
  36. require(bpa.header.magic == 0x50325748, "invalid magic value");
  37. bpa.header.version = encoded.toUint16(index);
  38. index += 2;
  39. require(bpa.header.version == 2, "invalid version");
  40. bpa.header.payloadId = encoded.toUint8(index);
  41. index += 1;
  42. // Payload ID of 2 required for batch header
  43. require(bpa.header.payloadId == 2, "invalid payload ID");
  44. // Parse the number of attestations
  45. bpa.nAttestations = encoded.toUint16(index);
  46. index += 2;
  47. // Parse the attestation size
  48. bpa.attestationSize = encoded.toUint16(index);
  49. index += 2;
  50. require(encoded.length == (index + (bpa.attestationSize * bpa.nAttestations)), "invalid BatchPriceAttestation size");
  51. bpa.attestations = new PythStructs.PriceAttestation[](bpa.nAttestations);
  52. // Deserialize each attestation
  53. for (uint j=0; j < bpa.nAttestations; j++) {
  54. // Header
  55. bpa.attestations[j].header.magic = encoded.toUint32(index);
  56. index += 4;
  57. require(bpa.attestations[j].header.magic == 0x50325748, "invalid magic value");
  58. bpa.attestations[j].header.version = encoded.toUint16(index);
  59. index += 2;
  60. require(bpa.attestations[j].header.version == 2, "invalid version");
  61. bpa.attestations[j].header.payloadId = encoded.toUint8(index);
  62. index += 1;
  63. // Payload ID of 1 required for individual attestation
  64. require(bpa.attestations[j].header.payloadId == 1, "invalid payload ID");
  65. // Attestation
  66. bpa.attestations[j].productId = encoded.toBytes32(index);
  67. index += 32;
  68. bpa.attestations[j].priceId = encoded.toBytes32(index);
  69. index += 32;
  70. bpa.attestations[j].priceType = encoded.toUint8(index);
  71. index += 1;
  72. bpa.attestations[j].price = int64(encoded.toUint64(index));
  73. index += 8;
  74. bpa.attestations[j].exponent = int32(encoded.toUint32(index));
  75. index += 4;
  76. bpa.attestations[j].emaPrice.value = int64(encoded.toUint64(index));
  77. index += 8;
  78. bpa.attestations[j].emaPrice.numerator = int64(encoded.toUint64(index));
  79. index += 8;
  80. bpa.attestations[j].emaPrice.denominator = int64(encoded.toUint64(index));
  81. index += 8;
  82. bpa.attestations[j].emaConf.value = int64(encoded.toUint64(index));
  83. index += 8;
  84. bpa.attestations[j].emaConf.numerator = int64(encoded.toUint64(index));
  85. index += 8;
  86. bpa.attestations[j].emaConf.denominator = int64(encoded.toUint64(index));
  87. index += 8;
  88. bpa.attestations[j].confidenceInterval = encoded.toUint64(index);
  89. index += 8;
  90. bpa.attestations[j].status = encoded.toUint8(index);
  91. index += 1;
  92. bpa.attestations[j].corpAct = encoded.toUint8(index);
  93. index += 1;
  94. bpa.attestations[j].timestamp = encoded.toUint64(index);
  95. index += 8;
  96. }
  97. }
  98. function parsePriceAttestation(bytes memory encodedPriceAttestation) public pure returns (PythStructs.PriceAttestation memory pa) {
  99. uint index = 0;
  100. pa.header.magic = encodedPriceAttestation.toUint32(index);
  101. index += 4;
  102. require(pa.header.magic == 0x50325748, "invalid protocol");
  103. pa.header.version = encodedPriceAttestation.toUint16(index);
  104. index += 2;
  105. require(pa.header.version == 1, "invalid protocol");
  106. pa.header.payloadId = encodedPriceAttestation.toUint8(index);
  107. index += 1;
  108. require(pa.header.payloadId == 1, "invalid PriceAttestation");
  109. pa.productId = encodedPriceAttestation.toBytes32(index);
  110. index += 32;
  111. pa.priceId = encodedPriceAttestation.toBytes32(index);
  112. index += 32;
  113. pa.priceType = encodedPriceAttestation.toUint8(index);
  114. index += 1;
  115. pa.price = int64(encodedPriceAttestation.toUint64(index));
  116. index += 8;
  117. pa.exponent = int32(encodedPriceAttestation.toUint32(index));
  118. index += 4;
  119. pa.emaPrice.value = int64(encodedPriceAttestation.toUint64(index));
  120. index += 8;
  121. pa.emaPrice.numerator = int64(encodedPriceAttestation.toUint64(index));
  122. index += 8;
  123. pa.emaPrice.denominator = int64(encodedPriceAttestation.toUint64(index));
  124. index += 8;
  125. pa.emaConf.value = int64(encodedPriceAttestation.toUint64(index));
  126. index += 8;
  127. pa.emaConf.numerator = int64(encodedPriceAttestation.toUint64(index));
  128. index += 8;
  129. pa.emaConf.denominator = int64(encodedPriceAttestation.toUint64(index));
  130. index += 8;
  131. pa.confidenceInterval = encodedPriceAttestation.toUint64(index);
  132. index += 8;
  133. pa.status = encodedPriceAttestation.toUint8(index);
  134. index += 1;
  135. pa.corpAct = encodedPriceAttestation.toUint8(index);
  136. index += 1;
  137. pa.timestamp = encodedPriceAttestation.toUint64(index);
  138. index += 8;
  139. require(encodedPriceAttestation.length == index, "invalid PriceAttestation");
  140. }
  141. }