diff --git a/contracts/cw-ics20-latest/artifacts/cw-ics20-latest.wasm b/contracts/cw-ics20-latest/artifacts/cw-ics20-latest.wasm index 32b0d7f..3fc7884 100644 Binary files a/contracts/cw-ics20-latest/artifacts/cw-ics20-latest.wasm and b/contracts/cw-ics20-latest/artifacts/cw-ics20-latest.wasm differ diff --git a/simulate-tests/.env.example b/simulate-tests/.env.example new file mode 100644 index 0000000..e69de29 diff --git a/simulate-tests/bridge-contract.spec.ts b/simulate-tests/bridge-contract.spec.ts index 8881236..909d900 100644 --- a/simulate-tests/bridge-contract.spec.ts +++ b/simulate-tests/bridge-contract.spec.ts @@ -1,5 +1,5 @@ import { Event, toBinary } from "@cosmjs/cosmwasm-stargate"; -import { Coin, coins } from "@cosmjs/proto-signing"; +import { Coin, coins, coin } from "@cosmjs/proto-signing"; import { CWSimulateApp, GenericError, @@ -63,6 +63,11 @@ describe.only("IBCModule", () => { "tron-testnet0xA325Ad6D9c92B55A3Fc5aD7e412B1518F96441C0"; let airiIbcDenom: string = "tron-testnet0x7e2A35C746F2f7C240B664F1Da4DD100141AE71F"; + let usdtIbcDenom: string = + "tron-testnet0xdac17f958d2ee523a2206206994597c13d831ec7"; + let AtomDenom = + "ibc/A2E2EEC9057A4A1C2C0A6A4C78B0239118DF5F278830F50B4A6BDD7A66506B78"; + let atomChannel = "channel-15"; let cosmosPort: string = "transfer"; let channel = "channel-0"; let ics20Contract: CwIcs20LatestClient; @@ -343,6 +348,7 @@ describe.only("IBCModule", () => { } }); + // TODO: test with native_token it.each([ [ { @@ -523,22 +529,12 @@ describe.only("IBCModule", () => { "empty-memo-should-fallback-to-transfer-to-receiver", ], [ - bobAddress, + parseToIbcWasmMemo(bobAddress, "", ""), ibcTransferAmount, "only-receiver-memo-should-fallback-to-transfer-to-receiver", ], [ - `${bobAddress}:`, - ibcTransferAmount, - "receiver-with-a-dot-memo-should-fallback-to-transfer-to-receiver", - ], - [ - `${oraib2oraichain}/${bobAddress}:`, - ibcTransferAmount, - "receiver-with-a-dot-and-channel-memo-should-fallback-to-transfer-to-receiver", - ], - [ - `${oraib2oraichain}/${bobAddress}`, + parseToIbcWasmMemo(bobAddress, oraib2oraichain, ""), ibcTransferAmount, "receiver-and-channel-memo-should-fallback-to-transfer-to-receiver", ], @@ -647,6 +643,9 @@ describe.only("IBCModule", () => { oraiSenderAddress, oracleAddress ); + + await oracleContract.updateTaxRate({ rate: "0" }); + await oracleContract.updateTaxCap({ denom: AtomDenom, cap: "100000" }); const { contractAddress: factoryAddress } = await oraidexArtifacts.deployContract( oraiClient, @@ -697,6 +696,17 @@ describe.only("IBCModule", () => { remoteDecimals: 6, localChannelId: channel, }); + await ics20Contract.updateMappingPair({ + localAssetInfo: { + token: { + contract_addr: usdtToken.contractAddress, + }, + }, + localAssetInfoDecimals: 6, + denom: usdtIbcDenom, + remoteDecimals: 6, + localChannelId: channel, + }); await factoryContract.createPair({ assetInfos, }); @@ -706,6 +716,17 @@ describe.only("IBCModule", () => { { token: { contract_addr: usdtToken.contractAddress } }, ], }); + await factoryContract.createPair({ + assetInfos: [ + assetInfos[0], + { + native_token: { + denom: AtomDenom, + }, + }, + ], + }); + const firstPairInfo = await factoryContract.pair({ assetInfos, }); @@ -715,6 +736,17 @@ describe.only("IBCModule", () => { { token: { contract_addr: usdtToken.contractAddress } }, ], }); + const thirdPairInfo = await factoryContract.pair({ + assetInfos: [ + assetInfos[0], + { + native_token: { + denom: AtomDenom, + }, + }, + ], + }); + // mint lots of orai, airi for the pair contracts to mock provide lp // here, ratio is 1:1 => 1 AIRI = 1 ORAI oraiClient.app.bank.setBalance( @@ -733,6 +765,10 @@ describe.only("IBCModule", () => { amount: initialBalanceAmount, recipient: secondPairInfo.contract_addr, }); + oraiClient.app.bank.setBalance(thirdPairInfo.contract_addr, [ + coin(initialBalanceAmount, ORAI), + coin(initialBalanceAmount, AtomDenom), + ]); }); it("test-simulate-withdraw-liquidity", async () => { @@ -851,12 +887,16 @@ describe.only("IBCModule", () => { it.each<[string, string, string]>([ [ - `${bobAddress}:orai`, + parseToIbcWasmMemo(bobAddress, "", "orai"), bobAddress, "Generic error: Destination channel empty in build ibc msg", ], [ - `channel-0/not-evm-based-nor-cosmos-based:orai`, + parseToIbcWasmMemo( + "not-evm-based-nor-cosmos-based", + channel, + oraiIbcDenom + ), bobAddress, "Generic error: The destination info is neither evm or cosmos based", ], @@ -867,6 +907,18 @@ describe.only("IBCModule", () => { expectedRecipient: string, expectedIbcErrorMsg: string ) => { + await ics20Contract.updateMappingPair({ + localAssetInfo: { + native_token: { + denom: ORAI, + }, + }, + localAssetInfoDecimals: 6, + denom: oraiIbcDenom, + remoteDecimals: 6, + localChannelId: channel, + }); + // now send ibc package icsPackage.memo = memo; // transfer from cosmos to oraichain, should pass @@ -915,13 +967,13 @@ describe.only("IBCModule", () => { ])( "cw-ics20-test-single-step-cw20-token-swap-operations-to-dest-denom memo %s dest denom %s expected recipient %s", async ( - memo: string, + destReceiver: string, destDenom: string, expectedRecipient: string, expectedIbcErrorMsg: string ) => { // now send ibc package - icsPackage.memo = memo + `:${destDenom}`; + icsPackage.memo = parseToIbcWasmMemo(destReceiver, "", destDenom); // transfer from cosmos to oraichain, should pass const result = await cosmosChain.ibc.sendPacketReceive({ packet: { @@ -946,7 +998,9 @@ describe.only("IBCModule", () => { it("cw-ics20-test-single-step-cw20-token-swap-operations-to-dest-denom-FAILED-cannot-simulate-swap", async () => { // now send ibc package - icsPackage.memo = `channel-0/${bobAddressEth}:foo`; + + // => dest token on Orai = ibc/EB7094899ACFB7A6F2A67DB084DEE2E9A83DEFAA5DEF92D9A9814FFD9FF673FA + icsPackage.memo = parseToIbcWasmMemo(bobAddressEth, "channel-0", "foo"); // transfer from cosmos to oraichain, should pass const result = await cosmosChain.ibc.sendPacketReceive({ packet: { @@ -958,7 +1012,7 @@ describe.only("IBCModule", () => { expect( result.attributes.find((attr) => attr.key === "ibc_error_msg").value ).toEqual( - 'Cannot simulate swap with ops: [OraiSwap { offer_asset_info: Token { contract_addr: Addr("orai18cvw806fj5n7xxz06ak8vjunveeks4zzzn37cu") }, ask_asset_info: NativeToken { denom: "orai" } }, OraiSwap { offer_asset_info: NativeToken { denom: "orai" }, ask_asset_info: NativeToken { denom: "foo" } }] with error: "Error parsing into type oraiswap::router::SimulateSwapOperationsResponse: unknown field `ok`, expected `amount`"' + 'Cannot simulate swap with ops: [OraiSwap { offer_asset_info: Token { contract_addr: Addr("orai18cvw806fj5n7xxz06ak8vjunveeks4zzzn37cu") }, ask_asset_info: NativeToken { denom: "orai" } }, OraiSwap { offer_asset_info: NativeToken { denom: "orai" }, ask_asset_info: NativeToken { denom: "ibc/EB7094899ACFB7A6F2A67DB084DEE2E9A83DEFAA5DEF92D9A9814FFD9FF673FA" } }] with error: "Error parsing into type oraiswap::router::SimulateSwapOperationsResponse: unknown field `ok`, expected `amount`"' ); }); @@ -999,7 +1053,13 @@ describe.only("IBCModule", () => { it("cw-ics20-test-single-step-cw20-FAILED-SWAP_OPS_FAILURE_ID-ack-SUCCESS", async () => { // fixture - icsPackage.memo = `${bobAddress}:${usdtToken.contractAddress}`; + // icsPackage.memo = `${bobAddress}:${usdtToken.contractAddress}`; + icsPackage.memo = parseToIbcWasmMemo( + bobAddress, + "", + usdtToken.contractAddress + ); + console.log(icsPackage.memo); icsPackage.amount = initialBalanceAmount + "0"; // transfer from cosmos to oraichain, should pass const result = await cosmosChain.ibc.sendPacketReceive({ @@ -1035,8 +1095,12 @@ describe.only("IBCModule", () => { it("cw-ics20-test-single-step-cw20-FAILED-IBC_TRANSFER_NATIVE_ERROR_ID-ack-SUCCESS", async () => { // fixture - icsPackage.memo = `unknown-channel/${bobAddress}:${usdtToken.contractAddress}`; + // icsPackage.memo = `unknown-channel/${bobAddress}:${usdtToken.contractAddress}`; + + // dest denom on orai: ibc/79E5EC9A42F2FC01B2BA609F13C985393779BE5153E01D24E79C2681B0DFB592 + icsPackage.memo = parseToIbcWasmMemo(bobAddress, "channel-15", "uatom"); icsPackage.amount = initialBalanceAmount; + // transfer from cosmos to oraichain, should pass const result = await cosmosChain.ibc.sendPacketReceive({ packet: { @@ -1045,6 +1109,7 @@ describe.only("IBCModule", () => { }, relayer: relayerAddress, }); + console.log(result); // refunding also fails because of not enough balance to refund expect( findWasmEvent(result.events, "action", "ibc_transfer_native_error_id") @@ -1088,7 +1153,12 @@ describe.only("IBCModule", () => { it("cw-ics20-test-single-step-cw20-FAILED-REFUND_FAILURE_ID-ack-SUCCESS", async () => { // fixture - icsPackage.memo = `${bobAddress}:${usdtToken.contractAddress}`; + + icsPackage.memo = parseToIbcWasmMemo( + bobAddress, + "", + usdtToken.contractAddress + ); icsPackage.amount = initialBalanceAmount + "0"; // transfer from cosmos to oraichain, should pass const result = await cosmosChain.ibc.sendPacketReceive({ @@ -1124,7 +1194,8 @@ describe.only("IBCModule", () => { it("cw-ics20-test-single-step-cw20-success-FOLLOW_UP_IBC_SEND_FAILURE_ID-must-not-have-SWAP_OPS_FAILURE_ID-or-on_packet_failure-ack-SUCCESS", async () => { // fixture - icsPackage.memo = `${channel}/${bobAddress}:${airiToken.contractAddress}`; + // icsPackage.memo = `${channel}/${bobAddress}:${airiToken.contractAddress}`; + icsPackage.memo = parseToIbcWasmMemo(bobAddress, channel, airiIbcDenom); icsPackage.amount = initialBalanceAmount; // transfer from cosmos to oraichain, should pass const result = await cosmosChain.ibc.sendPacketReceive({ @@ -1177,19 +1248,20 @@ describe.only("IBCModule", () => { }); it.each([ - [channel, "abcd", "orai1n6fwuamldz6mv5f3qwe9296pudjjemhmkfcgc3"], // hard-coded usdt address - [channel, "0x", "orai1n6fwuamldz6mv5f3qwe9296pudjjemhmkfcgc3"], - [channel, "0xabcd", "orai18cvw806fj5n7xxz06ak8vjunveeks4zzzn37cu"], - [ - channel, - "tron-testnet0xabcd", - "orai18cvw806fj5n7xxz06ak8vjunveeks4zzzn37cu", - ], // bad evm address case + [channel, "abcd", usdtIbcDenom], // hard-coded usdt address + [channel, "0x", airiIbcDenom], + [channel, "0xabcd", usdtIbcDenom], + [channel, "tron-testnet0xabcd", airiIbcDenom], // bad evm address case ])( "cw-ics20-test-single-step-has-ibc-msg-dest-fail memo %s dest denom %s expected error", async (destChannel: string, destReceiver: string, destDenom: string) => { // now send ibc package - icsPackage.memo = `${destChannel}/${destReceiver}:${destDenom}`; + // icsPackage.memo = `${destChannel}/${destReceiver}:${destDenom}`; + icsPackage.memo = parseToIbcWasmMemo( + destReceiver, + destChannel, + destDenom + ); // transfer from cosmos to oraichain, should pass const result = await cosmosChain.ibc.sendPacketReceive({ packet: { @@ -1216,12 +1288,17 @@ describe.only("IBCModule", () => { ); it.each([ - [channel, bridgeReceiver, "orai18cvw806fj5n7xxz06ak8vjunveeks4zzzn37cu"], // hard-coded airi + [channel, bridgeReceiver, airiIbcDenom], // hard-coded airi ])( "cw-ics20-test-single-step-has-ibc-msg-dest-receiver-evm-based memo %s dest denom %s expected recipient %s", async (destChannel: string, destReceiver: string, destDenom: string) => { // now send ibc package - icsPackage.memo = `${destChannel}/${destReceiver}:${destDenom}`; + // icsPackage.memo = `${destChannel}/${destReceiver}:${destDenom}`; + icsPackage.memo = parseToIbcWasmMemo( + destReceiver, + destChannel, + destDenom + ); // transfer from cosmos to oraichain, should pass let result = await cosmosChain.ibc.sendPacketReceive({ @@ -1300,7 +1377,8 @@ describe.only("IBCModule", () => { denom: oraiIbcDenom, receiver: bobAddress, sender: cosmosSenderAddress, - memo: `${bobAddress}:${airiToken.contractAddress}`, + // memo: `${bobAddress}:${airiToken.contractAddress}`, + memo: parseToIbcWasmMemo(bobAddress, "", airiToken.contractAddress), }; // transfer from cosmos to oraichain, should pass let result = await cosmosChain.ibc.sendPacketReceive({ @@ -1364,7 +1442,8 @@ describe.only("IBCModule", () => { denom: oraiIbcDenom, receiver: bobAddress, sender: cosmosSenderAddress, - memo: `${bobAddress}:orai`, + // memo: `${bobAddress}:orai`, + memo: parseToIbcWasmMemo(bobAddress, "", "orai"), }; // transfer from cosmos to oraichain, should pass let result = await cosmosChain.ibc.sendPacketReceive({ @@ -1393,7 +1472,8 @@ describe.only("IBCModule", () => { }); describe("test-single-step-cosmos-based-ibc-transfer-native", () => { - const unknownChannel = "unknown-channel"; + // unknowChannel is channel to cosmos + const unknownChannel = "channel-15"; beforeEach(async () => { // fixture // needs to fake a new ibc channel so that we can successfully do ibc transfer @@ -1452,16 +1532,7 @@ describe.only("IBCModule", () => { }); it.each([ - [ - "unknown-channel", - "orai1g4h64yjt0fvzv5v2j8tyfnpe5kmnetejvfgs7g", - "orai18cvw806fj5n7xxz06ak8vjunveeks4zzzn37cu", - ], // edge case, dest denom is also airi - [ - "unknown-channel", - "cosmos1g4h64yjt0fvzv5v2j8tyfnpe5kmnetejl67nlm", - ORAI, - ], + ["channel-15", "orai1g4h64yjt0fvzv5v2j8tyfnpe5kmnetejvfgs7g", "uatom"], // edge case, dest denom is also airi ])( "cw-ics20-test-single-step-has-ibc-msg-dest-receiver-cosmos-based dest channel %s dest denom %s expected recipient %s", async ( @@ -1470,7 +1541,12 @@ describe.only("IBCModule", () => { destDenom: string ) => { // now send ibc package - icsPackage.memo = `${destChannel}/${destReceiver}:${destDenom}`; + // icsPackage.memo = `${destChannel}/${destReceiver}:${destDenom}`; + icsPackage.memo = parseToIbcWasmMemo( + destReceiver, + destChannel, + destDenom + ); // transfer from cosmos to oraichain, should pass const result = await cosmosChain.ibc.sendPacketReceive({ packet: { @@ -1485,6 +1561,7 @@ describe.only("IBCModule", () => { event.type === "transfer" && event.attributes.find((attr) => attr.key === "channel") ); + // get swap operation event expect(ibcEvent).not.toBeUndefined(); expect( @@ -1498,7 +1575,7 @@ describe.only("IBCModule", () => { ).toEqual(ics20Contract.contractAddress); expect( ibcEvent.attributes.find((attr) => attr.key === "amount").value - ).toContain(destDenom); + ).toContain(AtomDenom); } ); @@ -1537,7 +1614,8 @@ describe.only("IBCModule", () => { denom: oraiIbcDenom, receiver: bobAddress, sender: cosmosSenderAddress, - memo: `${unknownChannel}/${bobAddress}:orai`, + // memo: `${unknownChannel}/${bobAddress}:orai`, + memo: parseToIbcWasmMemo(bobAddress, atomChannel, "uatom"), }; // transfer from cosmos to oraichain, should pass let result = await cosmosChain.ibc.sendPacketReceive({ @@ -1547,9 +1625,14 @@ describe.only("IBCModule", () => { }, relayer: relayerAddress, }); + console.dir(result, { depth: null }); + const transferEvent = result.events.find( - (event) => event.type === "transfer" + (event) => + event.type === "transfer" && + event.attributes.find((attr) => attr.key === "channel") ); + console.log(transferEvent); expect( transferEvent.attributes.filter( (attr) => attr.key === "recipient" && attr.value === bobAddress @@ -1557,8 +1640,7 @@ describe.only("IBCModule", () => { ).toBeGreaterThan(0); expect( transferEvent.attributes.filter( - (attr) => - attr.key === "amount" && attr.value === `${ibcTransferAmount}orai` + (attr) => attr.key === "amount" && attr.value.includes(AtomDenom) ).length ).toBeGreaterThan(0); expect( @@ -1659,7 +1741,7 @@ describe.only("IBCModule", () => { denom: airiIbcDenom, receiver: bobAddress, sender: oraibridgeSenderAddress, - memo: `${channel}/${bobAddress}:orai`, + memo: parseToIbcWasmMemo(bobAddress, channel, oraiIbcDenom), }; // transfer from cosmos to oraichain, should pass let result = await cosmosChain.ibc.sendPacketReceive({ @@ -1688,8 +1770,8 @@ describe.only("IBCModule", () => { }); it.each<[string, string]>([ - [`${channel}/${bobAddress}:orai`, "20000000"], - [`${bobAddress}:orai`, "10000000"], + [parseToIbcWasmMemo(bobAddress, channel, oraiIbcDenom), "20000000"], + [parseToIbcWasmMemo(bobAddress, "", "orai"), "10000000"], ])( "cw-ics20-test-single-step-ibc-handle_ibc_packet_receive_native_remote_chain-has-token-fee-should-be-deducted", async (memo, expectedTokenFee) => { @@ -1736,16 +1818,16 @@ describe.only("IBCModule", () => { it.each<[string, string, string]>([ [ - `${channel}/${bobAddress}:orai18cvw806fj5n7xxz06ak8vjunveeks4zzzn37cu`, + parseToIbcWasmMemo(bobAddress, channel, airiIbcDenom), "20000000", "100000", ], [ - `${channel}/${bridgeReceiver}:orai18cvw806fj5n7xxz06ak8vjunveeks4zzzn37cu`, + parseToIbcWasmMemo(bridgeReceiver, channel, airiIbcDenom), "20000000", "200000", ], // double deducted when there's an outgoing ibc msg after receiving the packet - [`${bobAddress}:orai`, "10000000", "100000"], + [parseToIbcWasmMemo(bobAddress, "", "orai"), "10000000", "100000"], ])( "test-handle_ibc_packet_receive_native_remote_chain-has-both-token-fee-and-relayer-fee-should-be-both-deducted-given memo %s should give expected token fee %s and expected relayer fee %s", async (memo, expectedTokenFee, expectedRelayerFee) => { @@ -1849,7 +1931,7 @@ describe.only("IBCModule", () => { denom: airiIbcDenom, receiver: bobAddress, sender: oraibridgeSenderAddress, - memo: `${bobAddress}:orai`, + memo: parseToIbcWasmMemo(bobAddress, "", "orai"), }; // transfer from cosmos to oraichain, should pass let result = await cosmosChain.ibc.sendPacketReceive({ diff --git a/simulate-tests/package.json b/simulate-tests/package.json index 22dde13..411d0f9 100644 --- a/simulate-tests/package.json +++ b/simulate-tests/package.json @@ -11,7 +11,7 @@ "@cosmjs/stargate": "^0.31.0", "@oraichain/common-contracts-build": "^1.0.35", "@oraichain/common-contracts-sdk": "^1.0.31", - "@oraichain/cw-simulate": "^2.8.77", + "@oraichain/cw-simulate": "^2.8.78", "@oraichain/oraidex-common": "^1.0.76", "@oraichain/oraidex-contracts-build": "1.0.22", "@oraichain/oraidex-contracts-sdk": "^1.0.40", diff --git a/simulate-tests/yarn.lock b/simulate-tests/yarn.lock index 8212a3d..ffbef77 100644 --- a/simulate-tests/yarn.lock +++ b/simulate-tests/yarn.lock @@ -1417,10 +1417,10 @@ resolved "https://registry.yarnpkg.com/@oraichain/common-contracts-sdk/-/common-contracts-sdk-1.0.31.tgz#595f93b168438d69d64896909b37855c9afc92fb" integrity sha512-s8H20RXy5gCnu3DnM7L5ClQyj2mdQpbSBpZrXCpIAX9qY0LKsDdZG3sYaDQ8+VN333jz9Pp/qGWdFSYD+6PBsg== -"@oraichain/cosmwasm-vm-js@^0.2.80": - version "0.2.80" - resolved "https://registry.yarnpkg.com/@oraichain/cosmwasm-vm-js/-/cosmwasm-vm-js-0.2.80.tgz#517e4243ce98a2f39c15bf4a2a29ad340c855eed" - integrity sha512-Rkijs17HScM7c18bB7RX1dIjQ3iJS7Z0r27r90I0voYBkahbbl+1OumkWqC+JS+i5CN04Qjov72Vh3eYlzaK9Q== +"@oraichain/cosmwasm-vm-js@^0.2.81": + version "0.2.81" + resolved "https://registry.yarnpkg.com/@oraichain/cosmwasm-vm-js/-/cosmwasm-vm-js-0.2.81.tgz#1c3350ce1eac169bd94a51641267ec96eef89114" + integrity sha512-fvBpZKxC/B5eEmn754AUrdU0OtRUOEIOMAXJiI1HLcYjfj5bjtbiLJVjpHeMMNEXdUYU9tUqYJlhaDO50+wK8A== dependencies: "@cosmjs/amino" "^0.31.0" "@cosmjs/crypto" "^0.31.0" @@ -1431,17 +1431,17 @@ elliptic "^6.5.4" secp256k1 "^4.0.3" -"@oraichain/cw-simulate@^2.8.77": - version "2.8.77" - resolved "https://registry.yarnpkg.com/@oraichain/cw-simulate/-/cw-simulate-2.8.77.tgz#dbf5f0b9d1dc9e402ac12e0f19d329ddfe634c47" - integrity sha512-IXYqFzwRusGCQemTS82iyzcDDMqaSj/RyQt2fLQDqnGfQbfKcTnscJrQyjwhWT/ALTUrZCNJhyNE++A4LW1GZA== +"@oraichain/cw-simulate@^2.8.78": + version "2.8.78" + resolved "https://registry.yarnpkg.com/@oraichain/cw-simulate/-/cw-simulate-2.8.78.tgz#c448b4f18a0bbc982c1bc92e467babd07aaeb9f3" + integrity sha512-POYi7F2RFGOycoDTmBp6VJzYcWJjxu3w8Iv0hc7LdN4dc6YCrmDxxOepzFJlp9caJEWTH3CCMPaY4ieWM+k2hA== dependencies: "@cosmjs/amino" "^0.31.0" "@cosmjs/cosmwasm-stargate" "^0.31.0" "@cosmjs/crypto" "^0.31.0" "@cosmjs/encoding" "^0.31.0" "@kiruse/serde" "^0.8.0-rc.6" - "@oraichain/cosmwasm-vm-js" "^0.2.80" + "@oraichain/cosmwasm-vm-js" "^0.2.81" eventemitter3 "^5.0.0" protobufjs "^7.2.3" ts-results "^3.3.0"