diff --git a/pyth-evm-js/package.json b/pyth-evm-js/package.json index 0dc7080..eaa6b4e 100644 --- a/pyth-evm-js/package.json +++ b/pyth-evm-js/package.json @@ -17,6 +17,7 @@ "build": "tsc", "example-client": "npm run build && node lib/examples/EvmPriceServiceClient.js", "example-relay": "npm run build && node lib/examples/EvmRelay.js", + "example-wormhole": "npm run build && node lib/examples/WormholeTest.js", "format": "prettier --write \"src/**/*.ts\"", "lint": "eslint src/", "prepare": "npm run build", diff --git a/pyth-evm-js/src/IWormholeAbi.json b/pyth-evm-js/src/IWormholeAbi.json new file mode 100644 index 0000000..b1f3204 --- /dev/null +++ b/pyth-evm-js/src/IWormholeAbi.json @@ -0,0 +1,513 @@ +[ + { + "inputs": [], + "name": "chainId", + "outputs": [ + { + "internalType": "uint16", + "name": "", + "type": "uint16" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "getCurrentGuardianSetIndex", + "outputs": [ + { + "internalType": "uint32", + "name": "", + "type": "uint32" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint32", + "name": "index", + "type": "uint32" + } + ], + "name": "getGuardianSet", + "outputs": [ + { + "components": [ + { + "internalType": "address[]", + "name": "keys", + "type": "address[]" + }, + { + "internalType": "uint32", + "name": "expirationTime", + "type": "uint32" + } + ], + "internalType": "struct ReceiverStructs.GuardianSet", + "name": "", + "type": "tuple" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "getGuardianSetExpiry", + "outputs": [ + { + "internalType": "uint32", + "name": "", + "type": "uint32" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bytes32", + "name": "hash", + "type": "bytes32" + } + ], + "name": "governanceActionIsConsumed", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "governanceChainId", + "outputs": [ + { + "internalType": "uint16", + "name": "", + "type": "uint16" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "governanceContract", + "outputs": [ + { + "internalType": "bytes32", + "name": "", + "type": "bytes32" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "impl", + "type": "address" + } + ], + "name": "isInitialized", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bytes", + "name": "encodedVM", + "type": "bytes" + } + ], + "name": "parseAndVerifyVM", + "outputs": [ + { + "components": [ + { + "internalType": "uint8", + "name": "version", + "type": "uint8" + }, + { + "internalType": "uint32", + "name": "timestamp", + "type": "uint32" + }, + { + "internalType": "uint32", + "name": "nonce", + "type": "uint32" + }, + { + "internalType": "uint16", + "name": "emitterChainId", + "type": "uint16" + }, + { + "internalType": "bytes32", + "name": "emitterAddress", + "type": "bytes32" + }, + { + "internalType": "uint64", + "name": "sequence", + "type": "uint64" + }, + { + "internalType": "uint8", + "name": "consistencyLevel", + "type": "uint8" + }, + { + "internalType": "bytes", + "name": "payload", + "type": "bytes" + }, + { + "internalType": "uint32", + "name": "guardianSetIndex", + "type": "uint32" + }, + { + "components": [ + { + "internalType": "bytes32", + "name": "r", + "type": "bytes32" + }, + { + "internalType": "bytes32", + "name": "s", + "type": "bytes32" + }, + { + "internalType": "uint8", + "name": "v", + "type": "uint8" + }, + { + "internalType": "uint8", + "name": "guardianIndex", + "type": "uint8" + } + ], + "internalType": "struct ReceiverStructs.Signature[]", + "name": "signatures", + "type": "tuple[]" + }, + { + "internalType": "bytes32", + "name": "hash", + "type": "bytes32" + } + ], + "internalType": "struct ReceiverStructs.VM", + "name": "vm", + "type": "tuple" + }, + { + "internalType": "bool", + "name": "valid", + "type": "bool" + }, + { + "internalType": "string", + "name": "reason", + "type": "string" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bytes", + "name": "encodedVM", + "type": "bytes" + } + ], + "name": "parseVM", + "outputs": [ + { + "components": [ + { + "internalType": "uint8", + "name": "version", + "type": "uint8" + }, + { + "internalType": "uint32", + "name": "timestamp", + "type": "uint32" + }, + { + "internalType": "uint32", + "name": "nonce", + "type": "uint32" + }, + { + "internalType": "uint16", + "name": "emitterChainId", + "type": "uint16" + }, + { + "internalType": "bytes32", + "name": "emitterAddress", + "type": "bytes32" + }, + { + "internalType": "uint64", + "name": "sequence", + "type": "uint64" + }, + { + "internalType": "uint8", + "name": "consistencyLevel", + "type": "uint8" + }, + { + "internalType": "bytes", + "name": "payload", + "type": "bytes" + }, + { + "internalType": "uint32", + "name": "guardianSetIndex", + "type": "uint32" + }, + { + "components": [ + { + "internalType": "bytes32", + "name": "r", + "type": "bytes32" + }, + { + "internalType": "bytes32", + "name": "s", + "type": "bytes32" + }, + { + "internalType": "uint8", + "name": "v", + "type": "uint8" + }, + { + "internalType": "uint8", + "name": "guardianIndex", + "type": "uint8" + } + ], + "internalType": "struct ReceiverStructs.Signature[]", + "name": "signatures", + "type": "tuple[]" + }, + { + "internalType": "bytes32", + "name": "hash", + "type": "bytes32" + } + ], + "internalType": "struct ReceiverStructs.VM", + "name": "vm", + "type": "tuple" + } + ], + "stateMutability": "pure", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bytes32", + "name": "hash", + "type": "bytes32" + }, + { + "components": [ + { + "internalType": "bytes32", + "name": "r", + "type": "bytes32" + }, + { + "internalType": "bytes32", + "name": "s", + "type": "bytes32" + }, + { + "internalType": "uint8", + "name": "v", + "type": "uint8" + }, + { + "internalType": "uint8", + "name": "guardianIndex", + "type": "uint8" + } + ], + "internalType": "struct ReceiverStructs.Signature[]", + "name": "signatures", + "type": "tuple[]" + }, + { + "components": [ + { + "internalType": "address[]", + "name": "keys", + "type": "address[]" + }, + { + "internalType": "uint32", + "name": "expirationTime", + "type": "uint32" + } + ], + "internalType": "struct ReceiverStructs.GuardianSet", + "name": "guardianSet", + "type": "tuple" + } + ], + "name": "verifySignatures", + "outputs": [ + { + "internalType": "bool", + "name": "valid", + "type": "bool" + }, + { + "internalType": "string", + "name": "reason", + "type": "string" + } + ], + "stateMutability": "pure", + "type": "function" + }, + { + "inputs": [ + { + "components": [ + { + "internalType": "uint8", + "name": "version", + "type": "uint8" + }, + { + "internalType": "uint32", + "name": "timestamp", + "type": "uint32" + }, + { + "internalType": "uint32", + "name": "nonce", + "type": "uint32" + }, + { + "internalType": "uint16", + "name": "emitterChainId", + "type": "uint16" + }, + { + "internalType": "bytes32", + "name": "emitterAddress", + "type": "bytes32" + }, + { + "internalType": "uint64", + "name": "sequence", + "type": "uint64" + }, + { + "internalType": "uint8", + "name": "consistencyLevel", + "type": "uint8" + }, + { + "internalType": "bytes", + "name": "payload", + "type": "bytes" + }, + { + "internalType": "uint32", + "name": "guardianSetIndex", + "type": "uint32" + }, + { + "components": [ + { + "internalType": "bytes32", + "name": "r", + "type": "bytes32" + }, + { + "internalType": "bytes32", + "name": "s", + "type": "bytes32" + }, + { + "internalType": "uint8", + "name": "v", + "type": "uint8" + }, + { + "internalType": "uint8", + "name": "guardianIndex", + "type": "uint8" + } + ], + "internalType": "struct ReceiverStructs.Signature[]", + "name": "signatures", + "type": "tuple[]" + }, + { + "internalType": "bytes32", + "name": "hash", + "type": "bytes32" + } + ], + "internalType": "struct ReceiverStructs.VM", + "name": "vm", + "type": "tuple" + } + ], + "name": "verifyVM", + "outputs": [ + { + "internalType": "bool", + "name": "valid", + "type": "bool" + }, + { + "internalType": "string", + "name": "reason", + "type": "string" + } + ], + "stateMutability": "view", + "type": "function" + } +] diff --git a/pyth-evm-js/src/examples/EvmRelay.ts b/pyth-evm-js/src/examples/EvmRelay.ts index cb411c9..0b99d11 100644 --- a/pyth-evm-js/src/examples/EvmRelay.ts +++ b/pyth-evm-js/src/examples/EvmRelay.ts @@ -79,7 +79,7 @@ async function run() { .call(); console.log(`Update fee: ${updateFee}`); - pythContract.methods + await pythContract.methods .updatePriceFeeds(priceFeedUpdateData) .send({ value: updateFee }) .on("transactionHash", (hash: string) => { diff --git a/pyth-evm-js/src/examples/WormholeTest.ts b/pyth-evm-js/src/examples/WormholeTest.ts new file mode 100644 index 0000000..82098d0 --- /dev/null +++ b/pyth-evm-js/src/examples/WormholeTest.ts @@ -0,0 +1,98 @@ +import Web3 from "web3"; +import yargs from "yargs"; +import { hideBin } from "yargs/helpers"; + +import { EvmPriceServiceConnection } from "../index"; +import HDWalletProvider from "@truffle/hdwallet-provider"; +import IWormhole from "../IWormholeAbi.json"; + +const argv = yargs(hideBin(process.argv)) + .option("network", { + description: "RPC of the network to relay on.", + type: "string", + required: true, + }) + .option("endpoint", { + description: + "Endpoint URL for the price service. e.g: https://endpoint/example", + type: "string", + required: true, + }) + // This is actually the wormhole contract address (sorry, it's a hack) + .option("pyth-contract", { + description: "Pyth contract address.", + type: "string", + required: true, + }) + .option("price-ids", { + description: + "Space separated price feed ids (in hex) to fetch" + + " e.g: 0xf9c0172ba10dfa4d19088d...", + type: "array", + required: true, + }) + .option("mnemonic", { + description: "Mnemonic (private key) for sender", + type: "string", + required: true, + }) + .help() + .alias("help", "h") + .parserConfiguration({ + "parse-numbers": false, + }) + .parseSync(); + +const network = argv.network; +const pythContractAddr = argv.pythContract; + +const connection = new EvmPriceServiceConnection(argv.endpoint); + +async function run() { + const provider = new HDWalletProvider({ + mnemonic: { + phrase: argv.mnemonic, + }, + providerOrUrl: network, + }); + + const web3 = new Web3(provider); + const priceIds = argv.priceIds as string[]; + + const priceFeeds = await connection.getLatestPriceFeeds(priceIds); + console.log(priceFeeds); + + const priceFeedUpdateData = await connection.getPriceFeedsUpdateData( + priceIds + ); + console.log(priceFeedUpdateData); + + const wormholeContract = new web3.eth.Contract( + IWormhole as any, + pythContractAddr, + { + from: provider.getAddress(0), + } + ); + console.log("Made contract"); + + const chainId = await wormholeContract.methods.chainId().call(); + console.log(`chainId: ${chainId}`) + + const guardianSetIndex = await wormholeContract.methods.getCurrentGuardianSetIndex().call(); + console.log(`guardianSetIndex: ${JSON.stringify(guardianSetIndex)}`); + + const result = await wormholeContract.methods.getGuardianSet(guardianSetIndex).call(); + console.log(`result: ${JSON.stringify(result)}`); + + const parsedVm = await wormholeContract.methods.parseVM(priceFeedUpdateData[0]).call(); + console.log(`parseVM result: ${JSON.stringify(parsedVm)}`); + + // This call fails + const res = await wormholeContract.methods.parseAndVerifyVM(priceFeedUpdateData[0]).call(); + console.log(`parseAndVerifyVM result: ${JSON.stringify(res)}`); + + provider.engine.stop(); +} + +run(); diff --git a/pyth-evm-js/zkevm-wormhole.sh b/pyth-evm-js/zkevm-wormhole.sh new file mode 100755 index 0000000..981196d --- /dev/null +++ b/pyth-evm-js/zkevm-wormhole.sh @@ -0,0 +1,7 @@ +npm run example-wormhole -- \ + --network "https://rpc.public.zkevm-test.net/" \ + --pyth-contract "0x7e8fe3Cad4Bc3762D245f91E4e058Abc79F27B66" \ + --mnemonic "$MNEMONIC" \ + --endpoint https://xc-testnet.pyth.network \ + --price-ids \ + "0xf9c0172ba10dfa4d19088d94f5bf61d3b54d5bd7483a322a982e1373ee8ea31b"