Pārlūkot izejas kodu

Abehjati/eth-update-interface (#301)

* Update contract interface to the latest sdk

* Add migration step

* Fix eth2 chainid change

* Address reviews
Ali Behjati 3 gadi atpakaļ
vecāks
revīzija
9a285cf799

+ 1 - 2
devnet/eth-devnet.yaml

@@ -128,8 +128,7 @@ spec:
           command:
             - /bin/sh
             - -c
-            - "sed -i 's/CHAIN_ID=0x2/CHAIN_ID=0x4/g' .env &&
-               npm run migrate -- --network development &&
+            - "npm run migrate -- --network development &&
                nc -lkp 2000 0.0.0.0"
           readinessProbe:
             periodSeconds: 1

+ 2 - 0
ethereum/.env.prod.arbitrum

@@ -9,3 +9,5 @@ PYTHNET_CHAIN_ID=0x1a
 PYTHNET_EMITTER=0xf8cd23c2ab91237730770bbea08d61005cdda0984348f3f6eecb559638c0bba0
 
 VALID_TIME_PERIOD_SECONDS=60
+
+WORMHOLE_CHAIN_NAME=arbitrum

+ 1 - 0
ethereum/.env.prod.aurora

@@ -9,3 +9,4 @@ PYTHNET_CHAIN_ID=0x1a
 PYTHNET_EMITTER=0xf8cd23c2ab91237730770bbea08d61005cdda0984348f3f6eecb559638c0bba0
 
 VALID_TIME_PERIOD_SECONDS=60
+WORMHOLE_CHAIN_NAME=aurora

+ 2 - 0
ethereum/.env.prod.aurora_testnet

@@ -12,3 +12,5 @@ VALID_TIME_PERIOD_SECONDS=60
 
 GOVERNANCE_CHAIN_ID=0x1
 GOVERNANCE_EMITTER=0xad070b5dc9c72dcf0484b8f9c643f18586eadfbff06b7e240e4a540edb93d152
+
+WORMHOLE_CHAIN_NAME=aurora

+ 2 - 0
ethereum/.env.prod.avalanche

@@ -9,3 +9,5 @@ PYTHNET_CHAIN_ID=0x1a
 PYTHNET_EMITTER=0xf8cd23c2ab91237730770bbea08d61005cdda0984348f3f6eecb559638c0bba0
 
 VALID_TIME_PERIOD_SECONDS=60
+
+WORMHOLE_CHAIN_NAME=avalanche

+ 2 - 0
ethereum/.env.prod.bnb

@@ -9,3 +9,5 @@ PYTHNET_CHAIN_ID=0x1a
 PYTHNET_EMITTER=0xf8cd23c2ab91237730770bbea08d61005cdda0984348f3f6eecb559638c0bba0
 
 VALID_TIME_PERIOD_SECONDS=60
+
+WORMHOLE_CHAIN_NAME=bsc

+ 2 - 0
ethereum/.env.prod.bnb_testnet

@@ -12,3 +12,5 @@ VALID_TIME_PERIOD_SECONDS=60
 
 GOVERNANCE_CHAIN_ID=0x1
 GOVERNANCE_EMITTER=0xad070b5dc9c72dcf0484b8f9c643f18586eadfbff06b7e240e4a540edb93d152
+
+WORMHOLE_CHAIN_NAME=bsc

+ 2 - 0
ethereum/.env.prod.celo

@@ -9,3 +9,5 @@ PYTHNET_CHAIN_ID=0x1a
 PYTHNET_EMITTER=0xf8cd23c2ab91237730770bbea08d61005cdda0984348f3f6eecb559638c0bba0
 
 VALID_TIME_PERIOD_SECONDS=60
+
+WORMHOLE_CHAIN_NAME=celo

+ 2 - 0
ethereum/.env.prod.celo_alfajores_testnet

@@ -12,3 +12,5 @@ VALID_TIME_PERIOD_SECONDS=60
 
 GOVERNANCE_CHAIN_ID=0x1
 GOVERNANCE_EMITTER=0xad070b5dc9c72dcf0484b8f9c643f18586eadfbff06b7e240e4a540edb93d152
+
+WORMHOLE_CHAIN_NAME=celo

+ 2 - 0
ethereum/.env.prod.development

@@ -13,3 +13,5 @@ VALID_TIME_PERIOD_SECONDS=60
 
 GOVERNANCE_CHAIN_ID=0x1
 GOVERNANCE_EMITTER=0xad070b5dc9c72dcf0484b8f9c643f18586eadfbff06b7e240e4a540edb93d152
+
+WORMHOLE_CHAIN_NAME=ethereum

+ 2 - 0
ethereum/.env.prod.fantom

@@ -9,3 +9,5 @@ PYTHNET_CHAIN_ID=0x1a
 PYTHNET_EMITTER=0xf8cd23c2ab91237730770bbea08d61005cdda0984348f3f6eecb559638c0bba0
 
 VALID_TIME_PERIOD_SECONDS=60
+
+WORMHOLE_CHAIN_NAME=fantom

+ 2 - 0
ethereum/.env.prod.fantom_testnet

@@ -12,3 +12,5 @@ VALID_TIME_PERIOD_SECONDS=60
 
 GOVERNANCE_CHAIN_ID=0x1
 GOVERNANCE_EMITTER=0xad070b5dc9c72dcf0484b8f9c643f18586eadfbff06b7e240e4a540edb93d152
+
+WORMHOLE_CHAIN_NAME=fantom

+ 2 - 0
ethereum/.env.prod.fuji

@@ -14,3 +14,5 @@ VALID_TIME_PERIOD_SECONDS=60
 
 GOVERNANCE_CHAIN_ID=0x1
 GOVERNANCE_EMITTER=0xad070b5dc9c72dcf0484b8f9c643f18586eadfbff06b7e240e4a540edb93d152
+
+WORMHOLE_CHAIN_NAME=avalanche

+ 2 - 0
ethereum/.env.prod.goerli

@@ -12,3 +12,5 @@ VALID_TIME_PERIOD_SECONDS=120
 
 GOVERNANCE_CHAIN_ID=0x1
 GOVERNANCE_EMITTER=0xad070b5dc9c72dcf0484b8f9c643f18586eadfbff06b7e240e4a540edb93d152
+
+WORMHOLE_CHAIN_NAME=mainnet

+ 2 - 0
ethereum/.env.prod.mainnet

@@ -9,3 +9,5 @@ PYTHNET_CHAIN_ID=0x1a
 PYTHNET_EMITTER=0xf8cd23c2ab91237730770bbea08d61005cdda0984348f3f6eecb559638c0bba0
 
 VALID_TIME_PERIOD_SECONDS=120
+
+WORMHOLE_CHAIN_NAME=ethereum

+ 2 - 0
ethereum/.env.prod.mumbai

@@ -14,3 +14,5 @@ VALID_TIME_PERIOD_SECONDS=60
 
 GOVERNANCE_CHAIN_ID=0x1
 GOVERNANCE_EMITTER=0xad070b5dc9c72dcf0484b8f9c643f18586eadfbff06b7e240e4a540edb93d152
+
+WORMHOLE_CHAIN_NAME=polygon

+ 2 - 0
ethereum/.env.prod.optimism

@@ -9,3 +9,5 @@ PYTHNET_CHAIN_ID=0x1a
 PYTHNET_EMITTER=0xf8cd23c2ab91237730770bbea08d61005cdda0984348f3f6eecb559638c0bba0
 
 VALID_TIME_PERIOD_SECONDS=60
+
+WORMHOLE_CHAIN_NAME=optimism

+ 2 - 0
ethereum/.env.prod.polygon

@@ -9,3 +9,5 @@ PYTHNET_CHAIN_ID=0x1a
 PYTHNET_EMITTER=0xf8cd23c2ab91237730770bbea08d61005cdda0984348f3f6eecb559638c0bba0
 
 VALID_TIME_PERIOD_SECONDS=60
+
+WORMHOLE_CHAIN_NAME=polygon

+ 2 - 0
ethereum/.env.prod.ropsten

@@ -12,3 +12,5 @@ VALID_TIME_PERIOD_SECONDS=120
 
 GOVERNANCE_CHAIN_ID=0x1
 GOVERNANCE_EMITTER=0xad070b5dc9c72dcf0484b8f9c643f18586eadfbff06b7e240e4a540edb93d152
+
+WORMHOLE_CHAIN_NAME=ropsten

+ 3 - 1
ethereum/.env.template

@@ -26,4 +26,6 @@ PYTHNET_EMITTER= # 0xa27839d641b07743c0cb5f68c51f8cd31d2c0762bec00dc6fcd25433ef1
 VALID_TIME_PERIOD_SECONDS= # 60
 
 GOVERNANCE_CHAIN_ID= # 0x1
-GOVERNANCE_EMITTER = # 0xa27839d641b07743c0cb5f68c51f8cd31d2c0762bec00dc6fcd25433ef1ab5b6
+GOVERNANCE_EMITTER= # 0xa27839d641b07743c0cb5f68c51f8cd31d2c0762bec00dc6fcd25433ef1ab5b6
+
+WORMHOLE_CHAIN_NAME= # ethereum, defined in https://github.com/wormhole-foundation/wormhole/blob/dev.v2/sdk/js/src/utils/consts.ts

+ 2 - 0
ethereum/.env.test

@@ -28,3 +28,5 @@ VALID_TIME_PERIOD_SECONDS=60
 
 GOVERNANCE_CHAIN_ID=0x1
 GOVERNANCE_EMITTER=0x0000000000000000000000000000000000000000000000000000000000001234
+
+WORMHOLE_CHAIN_NAME=ethereum

+ 9 - 2
ethereum/Deploying.md

@@ -23,11 +23,18 @@ npx apply-registry
 # If it is a new chain you are deploying to, create a new env file and commit it to the repo.
 rm -f .env; ln -s .env.prod.xyz .env && set -o allexport && source .env set && set +o allexport
 
-# Perform the migration
+# Perform the migration step by step using `--to <migration file number>` argument. Some steps require a governance execution to be successful. //TODO the process.
 # You might need to repeat the steps because of busy RPCs.
 # Also, sometimes the gases are not adjusted. Please update them with the network
 # explorer gas tracker.
-npx truffle migrate --network $MIGRATIONS_NETWORK
+npx truffle migrate --network $MIGRATIONS_NETWORK --to <step>
+
+# Some steps require executing a governance instruction to be successful, you can use the multisig message builder tool in 
+# `third_party/pyth` of this repo root to create multisig transaction and execute it to create the VAA.
+# Then you can use the VAA (in hex) to execute the governance instruction. To do so, run:
+$ npx truffle console --network $MIGRATIONS_NETWORK
+> let p = await PythUpgradable.deployed()
+> await p.executeGovernanceInstruction("<VAA in hex like: 0x123002342352>");
 
 # Perform this in first time mainnet deployments with Wormhole Receiver. (Or when guardian sets are upgraded)
 npm run receiver-submit-guardian-sets -- --network $MIGRATIONS_NETWORK

+ 30 - 21
ethereum/contracts/pyth/Pyth.sol

@@ -36,18 +36,19 @@ abstract contract Pyth is PythGetters, PythSetters, AbstractPyth {
 
         for (uint i = 0; i < batch.attestations.length; i++) {
             PythInternalStructs.PriceAttestation memory attestation = batch.attestations[i];
-
+    
+            PythInternalStructs.PriceInfo memory newPriceInfo = createNewPriceInfo(attestation);
             PythInternalStructs.PriceInfo memory latestPrice = latestPriceInfo(attestation.priceId);
 
             bool fresh = false;
-            if(attestation.attestationTime > latestPrice.attestationTime) {
+            if(newPriceInfo.priceFeed.price.publishTime > latestPrice.priceFeed.price.publishTime) {
                 freshPrices += 1;
                 fresh = true;
-                setLatestPriceInfo(attestation.priceId, newPriceInfo(attestation));
+                setLatestPriceInfo(attestation.priceId, newPriceInfo);
             }
 
-            emit PriceFeedUpdate(attestation.priceId, fresh, vm.emitterChainId, vm.sequence, latestPrice.priceFeed.publishTime,
-                attestation.publishTime, attestation.price, attestation.conf);
+            emit PriceFeedUpdate(attestation.priceId, fresh, vm.emitterChainId, vm.sequence, latestPrice.priceFeed.price.publishTime,
+                newPriceInfo.priceFeed.price.publishTime, newPriceInfo.priceFeed.price.price, newPriceInfo.priceFeed.price.conf);
         }
 
         emit BatchPriceFeedUpdate(vm.emitterChainId, vm.sequence, batch.attestations.length, freshPrices);
@@ -71,25 +72,33 @@ abstract contract Pyth is PythGetters, PythSetters, AbstractPyth {
         return singleUpdateFeeInWei() * updateDataSize;
     }
 
-    function newPriceInfo(PythInternalStructs.PriceAttestation memory pa) private view returns (PythInternalStructs.PriceInfo memory info) {
+    function createNewPriceInfo(PythInternalStructs.PriceAttestation memory pa) private view returns (PythInternalStructs.PriceInfo memory info) {
         info.attestationTime = pa.attestationTime;
         info.arrivalTime = block.timestamp;
         info.arrivalBlock = block.number;
-
         info.priceFeed.id = pa.priceId;
-        info.priceFeed.price = pa.price;
-        info.priceFeed.conf = pa.conf;
-        info.priceFeed.expo = pa.expo;
-        info.priceFeed.status = PythStructs.PriceStatus(pa.status);
-        info.priceFeed.emaPrice = pa.emaPrice;
-        info.priceFeed.emaConf = pa.emaConf;
-        info.priceFeed.productId = pa.productId;
-        info.priceFeed.numPublishers = pa.numPublishers;
-        info.priceFeed.maxNumPublishers = pa.maxNumPublishers;
-        info.priceFeed.prevConf = pa.prevConf;
-        info.priceFeed.prevPublishTime = pa.prevPublishTime;
-        info.priceFeed.prevPrice = pa.prevPrice;
-        info.priceFeed.publishTime = pa.publishTime;
+
+        PythInternalStructs.PriceAttestationStatus status = PythInternalStructs.PriceAttestationStatus(pa.status);
+        if (status == PythInternalStructs.PriceAttestationStatus.TRADING) {
+            info.priceFeed.price.price = pa.price;
+            info.priceFeed.price.conf = pa.conf;
+            info.priceFeed.price.publishTime = pa.publishTime;
+            info.priceFeed.emaPrice.publishTime = pa.publishTime;
+        } else {
+            info.priceFeed.price.price = pa.prevPrice;
+            info.priceFeed.price.conf = pa.prevConf;
+            info.priceFeed.price.publishTime = pa.prevPublishTime;
+
+            // The EMA is last updated when the aggregate had trading status,
+            // so, we use prev_publish_time (the time when the aggregate last had trading status).
+            info.priceFeed.emaPrice.publishTime = pa.prevPublishTime;
+        }
+
+        info.priceFeed.price.expo = pa.expo;
+        info.priceFeed.emaPrice.price = pa.emaPrice;
+        info.priceFeed.emaPrice.conf = pa.emaConf;
+        info.priceFeed.emaPrice.expo = pa.expo;
+
         return info;
     }
 
@@ -232,7 +241,7 @@ abstract contract Pyth is PythGetters, PythSetters, AbstractPyth {
     }
 
     function version() public pure returns (string memory) {
-        return "1.0.0";
+        return "1.1.0";
     }
 
     function deployCommitHash() public pure returns (string memory) {

+ 13 - 0
ethereum/contracts/pyth/PythInternalStructs.sol

@@ -54,4 +54,17 @@ contract PythInternalStructs {
         uint16 chainId;
         bytes32 emitterAddress;
     }
+
+    /* PriceAttestationStatus represents the availability status of a price feed passed down in attestation.
+        UNKNOWN: The price feed is not currently updating for an unknown reason.
+        TRADING: The price feed is updating as expected.
+        HALTED: The price feed is not currently updating because trading in the product has been halted.
+        AUCTION: The price feed is not currently updating because an auction is setting the price.
+    */
+    enum PriceAttestationStatus {
+        UNKNOWN,
+        TRADING,
+        HALTED,
+        AUCTION
+    }
 }

+ 6 - 3
ethereum/contracts/pyth/PythState.sol

@@ -11,9 +11,8 @@ contract PythStorage {
         uint16 _deprecatedPyth2WormholeChainId; // Replaced by validDataSources/isValidDataSource
         bytes32 _deprecatedPyth2WormholeEmitter; // Ditto
 
-        // Mapping of cached price information
-        // priceId => PriceInfo
-        mapping(bytes32 => PythInternalStructs.PriceInfo) latestPriceInfo;
+        // After a backward-incompatible change in PriceFeed this mapping got deprecated.
+        mapping(bytes32 => PythInternalStructs.PriceInfo) _deprecatedLatestPriceInfo;
 
         // For tracking all active emitter/chain ID pairs
         PythInternalStructs.DataSource[] validDataSources;
@@ -36,6 +35,10 @@ contract PythStorage {
         // with a lower or equal sequence number will be discarded. This prevents double-execution,
         // and also makes sure that messages are executed in the right order.
         uint64 lastExecutedGovernanceSequence;
+
+        // Mapping of cached price information
+        // priceId => PriceInfo
+        mapping(bytes32 => PythInternalStructs.PriceInfo) latestPriceInfo;
     }
 }
 

+ 21 - 0
ethereum/migrations/prod-receiver/11_pyth_make_interface_simpler.js

@@ -0,0 +1,21 @@
+require('dotenv').config({ path: "../.env" });
+
+const PythUpgradable = artifacts.require("PythUpgradable");
+const governanceChainId = process.env.GOVERNANCE_CHAIN_ID;
+const governanceEmitter = process.env.GOVERNANCE_EMITTER;
+
+console.log("governanceEmitter: " + governanceEmitter);
+console.log("governanceChainId: " + governanceChainId);
+
+const { upgradeProxy } = require("@openzeppelin/truffle-upgrades");
+
+/**
+ * Version 1.1.0
+ * 
+ * This change:
+ * - Use pyth-sdk-solidity 1.0.0 which simplifies the PriceFeed interface
+ */
+module.exports = async function (deployer) {
+    const proxy = await PythUpgradable.deployed();
+    await upgradeProxy(proxy.address, PythUpgradable, { deployer, unsafeSkipStorageCheck: true });
+}

+ 34 - 0
ethereum/migrations/prod/10_pyth_make_interface_simpler.js

@@ -0,0 +1,34 @@
+require('dotenv').config({ path: "../.env" });
+
+const governance = require("@pythnetwork/xc-governance-sdk");
+
+const PythUpgradable = artifacts.require("PythUpgradable");
+const governanceChainId = process.env.GOVERNANCE_CHAIN_ID;
+const governanceEmitter = process.env.GOVERNANCE_EMITTER;
+const wormholeChainName = process.env.WORMHOLE_CHAIN_NAME;
+assert(governance.CHAINS[wormholeChainName] !== undefined);
+
+console.log("governanceEmitter: " + governanceEmitter);
+console.log("governanceChainId: " + governanceChainId);
+
+const { deployProxyImpl } = require('@openzeppelin/truffle-upgrades/dist/utils');
+const { assert } = require('chai');
+
+/**
+ * Version 1.1.0
+ * 
+ * This change:
+ * - Use pyth-sdk-solidity 1.0.0 which simplifies the PriceFeed interface
+ */
+module.exports = async function (deployer) {
+    const proxy = await PythUpgradable.deployed();
+    const newImpl = (await deployProxyImpl(PythUpgradable, { deployer, unsafeSkipStorageCheck: true }, proxy.address)).impl;
+    console.log(`New implementation address is: ${newImpl}. Please sign and execute the following encoded ` +
+        `governance instruction to upgrade it.`);
+
+    const instructionBuffer = new governance.EthereumUpgradeContractInstruction(
+        governance.CHAINS[wormholeChainName],
+        new governance.HexString20Bytes(newImpl)
+    ).serialize();
+    console.log(`Governance instruction: 0x${instructionBuffer.toString('hex')}`);
+}

+ 0 - 0
ethereum/migrations/prod/10_pyth_renounce_ownership.js → ethereum/migrations/prod/11_pyth_renounce_ownership.js


+ 35 - 0
ethereum/migrations/test/11_pyth_make_interface_simpler.js

@@ -0,0 +1,35 @@
+require('dotenv').config({ path: "../.env" });
+
+const governance = require("@pythnetwork/xc-governance-sdk");
+const createLocalnetGovernanceVaa = require("../../scripts/createLocalnetGovernanceVaa");
+
+const PythUpgradable = artifacts.require("PythUpgradable");
+const governanceChainId = process.env.GOVERNANCE_CHAIN_ID;
+const governanceEmitter = process.env.GOVERNANCE_EMITTER;
+
+console.log("governanceEmitter: " + governanceEmitter);
+console.log("governanceChainId: " + governanceChainId);
+
+const { deployProxyImpl } = require('@openzeppelin/truffle-upgrades/dist/utils');
+
+/**
+ * Version 1.1.0
+ * 
+ * This change:
+ * - Use pyth-sdk-solidity 1.0.0 which simplifies the PriceFeed interface
+ */
+module.exports = async function (deployer) {
+    const proxy = await PythUpgradable.deployed();
+    const newImpl = (await deployProxyImpl(PythUpgradable, { deployer, unsafeSkipStorageCheck: true }, proxy.address)).impl;
+    console.log(newImpl);
+
+    await proxy.executeGovernanceInstruction(
+        createLocalnetGovernanceVaa(
+            new governance.EthereumUpgradeContractInstruction(
+                governance.CHAINS.ethereum,
+                new governance.HexString20Bytes(newImpl)
+            ).serialize(),
+            1,
+        )
+    );
+}

Failā izmaiņas netiks attēlotas, jo tās ir par lielu
+ 46 - 156
ethereum/package-lock.json


+ 6 - 3
ethereum/package.json

@@ -1,6 +1,6 @@
 {
   "name": "@pythnetwork/pyth-evm-contract",
-  "version": "1.0.0",
+  "version": "1.1.0",
   "description": "",
   "devDependencies": {
     "@chainsafe/truffle-plugin-abigen": "0.0.1",
@@ -29,12 +29,15 @@
   "dependencies": {
     "@openzeppelin/contracts": "^4.5.0",
     "@openzeppelin/contracts-upgradeable": "^4.5.2",
-    "@pythnetwork/pyth-sdk-solidity": "^0.5.2",
+    "@pythnetwork/pyth-sdk-solidity": "^1.0.1",
     "@pythnetwork/xc-governance-sdk": "file:../third_party/pyth/xc-governance-sdk-js",
     "dotenv": "^10.0.0",
     "elliptic": "^6.5.2",
     "ganache-cli": "^6.12.1",
     "jsonfile": "^4.0.0",
-    "solc": "^0.8.4"
+    "solc": "^0.8.4",
+    "web3": "^1.2.2",
+    "web3-eth-abi": "^1.2.2",
+    "web3-utils": "^1.2.2"
   }
 }

+ 112 - 0
ethereum/scripts/createLocalnetGovernanceVaa.js

@@ -0,0 +1,112 @@
+const abi = require("web3-eth-abi");
+const utils = require("web3-utils");
+const elliptic = require("elliptic");
+
+const testSigner1PK = "cfb12303a19cde580bb4dd771639b0d26bc68353645571a8cff516ab2ee113a0";
+
+const testGovernanceChain = "1"; // ethereum
+const testGovernanceEmitter = "0x0000000000000000000000000000000000000000000000000000000000001234";
+
+function zeroPadBytes(value, length) {
+    while (value.length < 2 * length) {
+        value = "0" + value;
+    }
+    return value;
+}
+
+const signAndEncodeVM = function (
+    timestamp,
+    nonce,
+    emitterChainId,
+    emitterAddress,
+    sequence,
+    data,
+    signers,
+    guardianSetIndex,
+    consistencyLevel
+) {
+    const body = [
+        abi
+            .encodeParameter("uint32", timestamp)
+            .substring(2 + (64 - 8)),
+        abi.encodeParameter("uint32", nonce).substring(2 + (64 - 8)),
+        abi
+            .encodeParameter("uint16", emitterChainId)
+            .substring(2 + (64 - 4)),
+        abi.encodeParameter("bytes32", emitterAddress).substring(2),
+        abi
+            .encodeParameter("uint64", sequence)
+            .substring(2 + (64 - 16)),
+        abi
+            .encodeParameter("uint8", consistencyLevel)
+            .substring(2 + (64 - 2)),
+        data.substr(2),
+    ];
+
+    const hash = utils.soliditySha3(
+        utils.soliditySha3("0x" + body.join(""))
+    );
+
+    let signatures = "";
+
+    for (let i in signers) {
+        const ec = new elliptic.ec("secp256k1");
+        const key = ec.keyFromPrivate(signers[i]);
+        const signature = key.sign(hash.substr(2), { canonical: true });
+
+        const packSig = [
+            abi.encodeParameter("uint8", i).substring(2 + (64 - 2)),
+            zeroPadBytes(signature.r.toString(16), 32),
+            zeroPadBytes(signature.s.toString(16), 32),
+            abi
+                .encodeParameter("uint8", signature.recoveryParam)
+                .substr(2 + (64 - 2)),
+        ];
+
+        signatures += packSig.join("");
+    }
+
+    const vm = [
+        abi.encodeParameter("uint8", 1).substring(2 + (64 - 2)),
+        abi
+            .encodeParameter("uint32", guardianSetIndex)
+            .substring(2 + (64 - 8)),
+        abi
+            .encodeParameter("uint8", signers.length)
+            .substring(2 + (64 - 2)),
+
+        signatures,
+        body.join(""),
+    ].join("");
+
+    return vm;
+};
+
+function createVAAFromUint8Array(
+    dataBuffer,
+    emitterChainId,
+    emitterAddress,
+    sequence,
+) {
+    const dataHex = "0x" + dataBuffer.toString("hex");
+    return "0x" + signAndEncodeVM(
+        0,
+        0,
+        emitterChainId,
+        emitterAddress,
+        sequence,
+        dataHex,
+        [testSigner1PK],
+        0,
+        0
+    );
+}
+
+module.exports = function createLocalnetGovernanceVAA(dataBuffer, sequence) {
+    return createVAAFromUint8Array(
+        dataBuffer,
+        testGovernanceChain,
+        testGovernanceEmitter,
+        sequence
+    );
+}

Failā izmaiņas netiks attēlotas, jo tās ir par lielu
+ 3 - 0
ethereum/test/pyth.js


Daži faili netika attēloti, jo izmaiņu fails ir pārāk liels