clients/js: xpla client code (#1698)

* clients/js: xpla client code

* Tweak parameters

Change-Id: I693249ed0bf152a4414d2e0cabea842802110a89

* Testnet RPC is wrong in the client

Change-Id: I6c8a43e4fde301a94e6ec3b2199ef9f6cabf0b45

Co-authored-by: Bruce Riley <briley@jumptrading.com>
This commit is contained in:
Paul Noel 2022-10-11 19:30:09 +00:00 committed by GitHub
parent ff9b3af3fc
commit c34022ee6a
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 382 additions and 22 deletions

View File

@ -14,6 +14,7 @@ import {
getEmitterAddressTerra,
getEmitterAddressEth,
getEmitterAddressAlgorand,
getEmitterAddressNear,
isCosmWasmChain,
} from "@certusone/wormhole-sdk";
import { execute_solana } from "./solana";
@ -42,6 +43,7 @@ import { NETWORKS } from "./networks";
import base58 from "bs58";
import { execute_algorand } from "./algorand";
import { execute_injective } from "./injective";
import { execute_xpla } from "./xpla";
setDefaultWasm("node");
@ -300,18 +302,12 @@ yargs(hideBin(process.argv))
if (chain === "solana" || chain === "pythnet") {
// TODO: Create an isSolanaChain()
addr = await getEmitterAddressSolana(addr);
} else if (isTerraChain(chain)) {
} else if (isCosmWasmChain(chain)) {
addr = await getEmitterAddressTerra(addr);
} else if (chain === "algorand") {
addr = getEmitterAddressAlgorand(BigInt(addr));
} else if (chain === "near") {
if (network !== "MAINNET") {
throw Error(
`unable to look up near emitter address for ${network}`
);
}
addr =
"148410499d3fcda4dcfd68a1ebfcdddda16ab28326448d4aae4d2f0465cdfcb7";
addr = await getEmitterAddressNear(addr);
} else {
addr = getEmitterAddressEth(addr);
}
@ -670,6 +666,8 @@ yargs(hideBin(process.argv))
await execute_near(parsed_vaa.payload, vaa_hex, network);
} else if (chain === "injective") {
await execute_injective(parsed_vaa.payload, buf, network);
} else if (chain === "xpla") {
await execute_xpla(parsed_vaa.payload, buf, network);
} else if (chain === "osmosis") {
throw Error("OSMOSIS is not supported yet");
} else if (chain === "sui") {

View File

@ -105,6 +105,11 @@ const MAINNET = {
rpc: "http://api.pythnet.pyth.network:8899/",
key: get_env_var("SOLANA_KEY"),
},
xpla: {
rpc: "https://dimension-lcd.xpla.dev",
chain_id: "dimension_37-1",
key: get_env_var("XPLA_KEY"),
},
wormholechain: {
rpc: undefined,
key: undefined,
@ -230,6 +235,11 @@ const TESTNET = {
rpc: "https://api.pythtest.pyth.network/",
key: get_env_var("SOLANA_KEY_TESTNET"),
},
xpla: {
rpc: "https://cube-lcd.xpla.dev:443",
chain_id: "cube_47-5",
key: get_env_var("XPLA_KEY_TESTNET"),
},
wormholechain: {
rpc: undefined,
key: undefined,
@ -345,6 +355,11 @@ const DEVNET = {
rpc: undefined,
key: undefined,
},
xpla: {
rpc: undefined,
chain_id: undefined,
key: undefined,
},
wormholechain: {
rpc: "http://localhost:1319",
chain_id: "wormholechain",

View File

@ -9,13 +9,14 @@
"version": "1.0.0",
"dependencies": {
"@celo-tools/celo-ethers-wrapper": "^0.1.0",
"@certusone/wormhole-sdk": "^0.7.0",
"@certusone/wormhole-sdk": "^0.7.1",
"@cosmjs/encoding": "^0.26.2",
"@injectivelabs/networks": "^1.0.12",
"@injectivelabs/sdk-ts": "^1.0.75",
"@injectivelabs/tx-ts": "^1.0.22",
"@solana/web3.js": "^1.22.0",
"@terra-money/terra.js": "^3.1.3",
"@xpla/xpla.js": "^0.2.1",
"algosdk": "^1.15.0",
"axios": "^0.24.0",
"binary-parser": "^2.0.2",
@ -590,9 +591,9 @@
}
},
"node_modules/@certusone/wormhole-sdk": {
"version": "0.7.0",
"resolved": "https://registry.npmjs.org/@certusone/wormhole-sdk/-/wormhole-sdk-0.7.0.tgz",
"integrity": "sha512-pSNsgpyzoawhkrC3ONB+4ad0HdaOAXyLxgJLie2Mtisn9li9xxFN7Wf5vFXfTAcC0FAzdn/7G8NCVwDF3iF5wA==",
"version": "0.7.1",
"resolved": "https://registry.npmjs.org/@certusone/wormhole-sdk/-/wormhole-sdk-0.7.1.tgz",
"integrity": "sha512-G6V2x/0vYsYgr6WQAn/meaQWoac0R2V2Nqho0/vxpx7br0KsCbwD84Sy5iLZREThnP8fRMrFf03pVXpZRzEtOA==",
"dependencies": {
"@certusone/wormhole-sdk-proto-web": "^0.0.5",
"@certusone/wormhole-sdk-wasm": "^0.0.1",
@ -3412,6 +3413,75 @@
"node": ">=8"
}
},
"node_modules/@xpla/xpla.js": {
"version": "0.2.1",
"resolved": "https://registry.npmjs.org/@xpla/xpla.js/-/xpla.js-0.2.1.tgz",
"integrity": "sha512-+2v9/rxnRaaZBShT232bB3sow0JMe5ghIzLUPy7XJQasf6fu3mmVgMUIl9QCrILkuTZwh3yNsFOvkZTPH28Fmw==",
"dependencies": {
"@ethersproject/bytes": "^5.6.1",
"@ethersproject/keccak256": "^5.6.1",
"@ethersproject/signing-key": "^5.6.2",
"@terra-money/legacy.proto": "npm:@terra-money/terra.proto@^0.1.7",
"@terra-money/terra.proto": "^2.1.0",
"axios": "^0.26.1",
"bech32": "^2.0.0",
"bip32": "^2.0.6",
"bip39": "^3.0.3",
"bufferutil": "^4.0.3",
"crypto-addr-codec": "^0.1.7",
"decimal.js": "^10.2.1",
"elliptic": "^6.5.4",
"ethereumjs-util": "^7.1.5",
"jscrypto": "^1.0.1",
"readable-stream": "^3.6.0",
"secp256k1": "^4.0.2",
"tmp": "^0.2.1",
"utf-8-validate": "^5.0.5",
"ws": "^7.5.8"
},
"engines": {
"node": ">=14"
}
},
"node_modules/@xpla/xpla.js/node_modules/@terra-money/terra.proto": {
"version": "2.1.0",
"resolved": "https://registry.npmjs.org/@terra-money/terra.proto/-/terra.proto-2.1.0.tgz",
"integrity": "sha512-rhaMslv3Rkr+QsTQEZs64FKA4QlfO0DfQHaR6yct/EovenMkibDEQ63dEL6yJA6LCaEQGYhyVB9JO9pTUA8ybw==",
"dependencies": {
"@improbable-eng/grpc-web": "^0.14.1",
"google-protobuf": "^3.17.3",
"long": "^4.0.0",
"protobufjs": "~6.11.2"
}
},
"node_modules/@xpla/xpla.js/node_modules/axios": {
"version": "0.26.1",
"resolved": "https://registry.npmjs.org/axios/-/axios-0.26.1.tgz",
"integrity": "sha512-fPwcX4EvnSHuInCMItEhAGnaSEXRBjtzh9fOtsE6E1G6p7vl7edEeZe11QHf18+6+9gR5PbKV/sGKNaD8YaMeA==",
"dependencies": {
"follow-redirects": "^1.14.8"
}
},
"node_modules/@xpla/xpla.js/node_modules/bech32": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/bech32/-/bech32-2.0.0.tgz",
"integrity": "sha512-LcknSilhIGatDAsY1ak2I8VtGaHNhgMSYVxFrGLXv+xLHytaKZKcaUJJUE7qmBr7h33o5YQwP55pMI0xmkpJwg=="
},
"node_modules/@xpla/xpla.js/node_modules/ethereumjs-util": {
"version": "7.1.5",
"resolved": "https://registry.npmjs.org/ethereumjs-util/-/ethereumjs-util-7.1.5.tgz",
"integrity": "sha512-SDl5kKrQAudFBUe5OJM9Ac6WmMyYmXX/6sTmLZ3ffG2eY6ZIGBes3pEDxNN6V72WyOw4CPD5RomKdsa8DAAwLg==",
"dependencies": {
"@types/bn.js": "^5.1.0",
"bn.js": "^5.1.2",
"create-hash": "^1.1.2",
"ethereum-cryptography": "^0.1.3",
"rlp": "^2.2.4"
},
"engines": {
"node": ">=10.0.0"
}
},
"node_modules/101": {
"version": "1.6.3",
"license": "MIT",
@ -3773,6 +3843,14 @@
"resolved": "https://registry.npmjs.org/bech32/-/bech32-1.1.4.tgz",
"integrity": "sha512-s0IrSOzLlbvX7yp4WBfPITzpAU8sqQcpsmwXDiKwrG4r491vwCO/XpejasRNl0piBMe/DvP4Tz0mIS/X1DPJBQ=="
},
"node_modules/big-integer": {
"version": "1.6.36",
"resolved": "https://registry.npmjs.org/big-integer/-/big-integer-1.6.36.tgz",
"integrity": "sha512-t70bfa7HYEA1D9idDbmuv7YbsbVkQ+Hp+8KFSul4aE5e/i1bjCNIRYJZlA8Q8p0r9T8cF/RVvwUgRA//FydEyg==",
"engines": {
"node": ">=0.6"
}
},
"node_modules/bignumber.js": {
"version": "9.0.1",
"resolved": "https://registry.npmjs.org/bignumber.js/-/bignumber.js-9.0.1.tgz",
@ -4572,6 +4650,20 @@
"whatwg-fetch": "2.0.4"
}
},
"node_modules/crypto-addr-codec": {
"version": "0.1.7",
"resolved": "https://registry.npmjs.org/crypto-addr-codec/-/crypto-addr-codec-0.1.7.tgz",
"integrity": "sha512-X4hzfBzNhy4mAc3UpiXEC/L0jo5E8wAa9unsnA8nNXYzXjCcGk83hfC5avJWCSGT8V91xMnAS9AKMHmjw5+XCg==",
"dependencies": {
"base-x": "^3.0.8",
"big-integer": "1.6.36",
"blakejs": "^1.1.0",
"bs58": "^4.0.1",
"ripemd160-min": "0.0.6",
"safe-buffer": "^5.2.0",
"sha3": "^2.1.1"
}
},
"node_modules/crypto-browserify": {
"version": "3.12.0",
"resolved": "https://registry.npmjs.org/crypto-browserify/-/crypto-browserify-3.12.0.tgz",
@ -11769,6 +11861,14 @@
"inherits": "^2.0.1"
}
},
"node_modules/ripemd160-min": {
"version": "0.0.6",
"resolved": "https://registry.npmjs.org/ripemd160-min/-/ripemd160-min-0.0.6.tgz",
"integrity": "sha512-+GcJgQivhs6S9qvLogusiTcS9kQUfgR75whKuy5jIhuiOfQuJ8fjqxV6EGD5duH1Y/FawFUMtMhyeq3Fbnib8A==",
"engines": {
"node": ">=8"
}
},
"node_modules/rlp": {
"version": "2.2.6",
"resolved": "https://registry.npmjs.org/rlp/-/rlp-2.2.6.tgz",
@ -13456,9 +13556,9 @@
"integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8="
},
"node_modules/ws": {
"version": "7.5.7",
"resolved": "https://registry.npmjs.org/ws/-/ws-7.5.7.tgz",
"integrity": "sha512-KMvVuFzpKBuiIXW3E4u3mySRO2/mCHSyZDJQM5NQ9Q9KHWHWh0NHgfbRMLLrceUK5qAL4ytALJbpRMjixFZh8A==",
"version": "7.5.9",
"resolved": "https://registry.npmjs.org/ws/-/ws-7.5.9.tgz",
"integrity": "sha512-F+P9Jil7UiSKSkppIiD94dN07AwvFixvLIj1Og1Rl9GGMuNipJnV9JzjD6XuqmAeiswGvUmNLjr5cFuXwNS77Q==",
"engines": {
"node": ">=8.3.0"
},
@ -13995,9 +14095,9 @@
"requires": {}
},
"@certusone/wormhole-sdk": {
"version": "0.7.0",
"resolved": "https://registry.npmjs.org/@certusone/wormhole-sdk/-/wormhole-sdk-0.7.0.tgz",
"integrity": "sha512-pSNsgpyzoawhkrC3ONB+4ad0HdaOAXyLxgJLie2Mtisn9li9xxFN7Wf5vFXfTAcC0FAzdn/7G8NCVwDF3iF5wA==",
"version": "0.7.1",
"resolved": "https://registry.npmjs.org/@certusone/wormhole-sdk/-/wormhole-sdk-0.7.1.tgz",
"integrity": "sha512-G6V2x/0vYsYgr6WQAn/meaQWoac0R2V2Nqho0/vxpx7br0KsCbwD84Sy5iLZREThnP8fRMrFf03pVXpZRzEtOA==",
"requires": {
"@certusone/wormhole-sdk-proto-web": "^0.0.5",
"@certusone/wormhole-sdk-wasm": "^0.0.1",
@ -16067,6 +16167,71 @@
"tslib": "^2.3.0"
}
},
"@xpla/xpla.js": {
"version": "0.2.1",
"resolved": "https://registry.npmjs.org/@xpla/xpla.js/-/xpla.js-0.2.1.tgz",
"integrity": "sha512-+2v9/rxnRaaZBShT232bB3sow0JMe5ghIzLUPy7XJQasf6fu3mmVgMUIl9QCrILkuTZwh3yNsFOvkZTPH28Fmw==",
"requires": {
"@ethersproject/bytes": "^5.6.1",
"@ethersproject/keccak256": "^5.6.1",
"@ethersproject/signing-key": "^5.6.2",
"@terra-money/legacy.proto": "npm:@terra-money/terra.proto@^0.1.7",
"@terra-money/terra.proto": "^2.1.0",
"axios": "^0.26.1",
"bech32": "^2.0.0",
"bip32": "^2.0.6",
"bip39": "^3.0.3",
"bufferutil": "^4.0.3",
"crypto-addr-codec": "^0.1.7",
"decimal.js": "^10.2.1",
"elliptic": "^6.5.4",
"ethereumjs-util": "^7.1.5",
"jscrypto": "^1.0.1",
"readable-stream": "^3.6.0",
"secp256k1": "^4.0.2",
"tmp": "^0.2.1",
"utf-8-validate": "^5.0.5",
"ws": "^7.5.8"
},
"dependencies": {
"@terra-money/terra.proto": {
"version": "2.1.0",
"resolved": "https://registry.npmjs.org/@terra-money/terra.proto/-/terra.proto-2.1.0.tgz",
"integrity": "sha512-rhaMslv3Rkr+QsTQEZs64FKA4QlfO0DfQHaR6yct/EovenMkibDEQ63dEL6yJA6LCaEQGYhyVB9JO9pTUA8ybw==",
"requires": {
"@improbable-eng/grpc-web": "^0.14.1",
"google-protobuf": "^3.17.3",
"long": "^4.0.0",
"protobufjs": "~6.11.2"
}
},
"axios": {
"version": "0.26.1",
"resolved": "https://registry.npmjs.org/axios/-/axios-0.26.1.tgz",
"integrity": "sha512-fPwcX4EvnSHuInCMItEhAGnaSEXRBjtzh9fOtsE6E1G6p7vl7edEeZe11QHf18+6+9gR5PbKV/sGKNaD8YaMeA==",
"requires": {
"follow-redirects": "^1.14.8"
}
},
"bech32": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/bech32/-/bech32-2.0.0.tgz",
"integrity": "sha512-LcknSilhIGatDAsY1ak2I8VtGaHNhgMSYVxFrGLXv+xLHytaKZKcaUJJUE7qmBr7h33o5YQwP55pMI0xmkpJwg=="
},
"ethereumjs-util": {
"version": "7.1.5",
"resolved": "https://registry.npmjs.org/ethereumjs-util/-/ethereumjs-util-7.1.5.tgz",
"integrity": "sha512-SDl5kKrQAudFBUe5OJM9Ac6WmMyYmXX/6sTmLZ3ffG2eY6ZIGBes3pEDxNN6V72WyOw4CPD5RomKdsa8DAAwLg==",
"requires": {
"@types/bn.js": "^5.1.0",
"bn.js": "^5.1.2",
"create-hash": "^1.1.2",
"ethereum-cryptography": "^0.1.3",
"rlp": "^2.2.4"
}
}
}
},
"abstract-leveldown": {
"version": "2.6.3",
"resolved": "https://registry.npmjs.org/abstract-leveldown/-/abstract-leveldown-2.6.3.tgz",
@ -16342,6 +16507,11 @@
"resolved": "https://registry.npmjs.org/bech32/-/bech32-1.1.4.tgz",
"integrity": "sha512-s0IrSOzLlbvX7yp4WBfPITzpAU8sqQcpsmwXDiKwrG4r491vwCO/XpejasRNl0piBMe/DvP4Tz0mIS/X1DPJBQ=="
},
"big-integer": {
"version": "1.6.36",
"resolved": "https://registry.npmjs.org/big-integer/-/big-integer-1.6.36.tgz",
"integrity": "sha512-t70bfa7HYEA1D9idDbmuv7YbsbVkQ+Hp+8KFSul4aE5e/i1bjCNIRYJZlA8Q8p0r9T8cF/RVvwUgRA//FydEyg=="
},
"bignumber.js": {
"version": "9.0.1",
"resolved": "https://registry.npmjs.org/bignumber.js/-/bignumber.js-9.0.1.tgz",
@ -17018,6 +17188,20 @@
"whatwg-fetch": "2.0.4"
}
},
"crypto-addr-codec": {
"version": "0.1.7",
"resolved": "https://registry.npmjs.org/crypto-addr-codec/-/crypto-addr-codec-0.1.7.tgz",
"integrity": "sha512-X4hzfBzNhy4mAc3UpiXEC/L0jo5E8wAa9unsnA8nNXYzXjCcGk83hfC5avJWCSGT8V91xMnAS9AKMHmjw5+XCg==",
"requires": {
"base-x": "^3.0.8",
"big-integer": "1.6.36",
"blakejs": "^1.1.0",
"bs58": "^4.0.1",
"ripemd160-min": "0.0.6",
"safe-buffer": "^5.2.0",
"sha3": "^2.1.1"
}
},
"crypto-browserify": {
"version": "3.12.0",
"resolved": "https://registry.npmjs.org/crypto-browserify/-/crypto-browserify-3.12.0.tgz",
@ -22335,6 +22519,11 @@
"inherits": "^2.0.1"
}
},
"ripemd160-min": {
"version": "0.0.6",
"resolved": "https://registry.npmjs.org/ripemd160-min/-/ripemd160-min-0.0.6.tgz",
"integrity": "sha512-+GcJgQivhs6S9qvLogusiTcS9kQUfgR75whKuy5jIhuiOfQuJ8fjqxV6EGD5duH1Y/FawFUMtMhyeq3Fbnib8A=="
},
"rlp": {
"version": "2.2.6",
"resolved": "https://registry.npmjs.org/rlp/-/rlp-2.2.6.tgz",
@ -23658,9 +23847,9 @@
"integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8="
},
"ws": {
"version": "7.5.7",
"resolved": "https://registry.npmjs.org/ws/-/ws-7.5.7.tgz",
"integrity": "sha512-KMvVuFzpKBuiIXW3E4u3mySRO2/mCHSyZDJQM5NQ9Q9KHWHWh0NHgfbRMLLrceUK5qAL4ytALJbpRMjixFZh8A==",
"version": "7.5.9",
"resolved": "https://registry.npmjs.org/ws/-/ws-7.5.9.tgz",
"integrity": "sha512-F+P9Jil7UiSKSkppIiD94dN07AwvFixvLIj1Og1Rl9GGMuNipJnV9JzjD6XuqmAeiswGvUmNLjr5cFuXwNS77Q==",
"requires": {}
},
"xhr": {

View File

@ -3,13 +3,14 @@
"version": "1.0.0",
"dependencies": {
"@celo-tools/celo-ethers-wrapper": "^0.1.0",
"@certusone/wormhole-sdk": "^0.7.0",
"@certusone/wormhole-sdk": "^0.7.1",
"@cosmjs/encoding": "^0.26.2",
"@injectivelabs/networks": "^1.0.12",
"@injectivelabs/sdk-ts": "^1.0.75",
"@injectivelabs/tx-ts": "^1.0.22",
"@solana/web3.js": "^1.22.0",
"@terra-money/terra.js": "^3.1.3",
"@xpla/xpla.js": "^0.2.1",
"algosdk": "^1.15.0",
"axios": "^0.24.0",
"binary-parser": "^2.0.2",

157
clients/js/xpla.ts Normal file
View File

@ -0,0 +1,157 @@
import {
Coin,
Fee,
LCDClient,
MnemonicKey,
MsgExecuteContract,
} from "@xpla/xpla.js";
import { fromUint8Array } from "js-base64";
import { impossible, Payload } from "./vaa";
import { NETWORKS } from "./networks";
import { CONTRACTS } from "@certusone/wormhole-sdk";
import axios from "axios";
export async function execute_xpla(
payload: Payload,
vaa: Buffer,
network: "MAINNET" | "TESTNET" | "DEVNET"
) {
const chain = "xpla";
let n = NETWORKS[network][chain];
let contracts = CONTRACTS[network][chain];
const client = new LCDClient({
URL: n.rpc,
chainID: n.chain_id,
});
const wallet = client.wallet(
new MnemonicKey({
mnemonic: n.key,
})
);
let target_contract: string;
let execute_msg: object;
switch (payload.module) {
case "Core":
target_contract = contracts.core;
execute_msg = {
submit_v_a_a: {
vaa: fromUint8Array(vaa),
},
};
switch (payload.type) {
case "GuardianSetUpgrade":
console.log("Submitting new guardian set");
break;
case "ContractUpgrade":
console.log("Upgrading core contract");
break;
default:
impossible(payload);
}
break;
case "NFTBridge":
if (contracts.nft_bridge === undefined) {
// NOTE: this code can safely be removed once the terra NFT bridge is
// released, but it's fine for it to stay, as the condition will just be
// skipped once 'contracts.nft_bridge' is defined
throw new Error("NFT bridge not supported yet for terra");
}
target_contract = contracts.nft_bridge;
execute_msg = {
submit_vaa: {
data: fromUint8Array(vaa),
},
};
switch (payload.type) {
case "ContractUpgrade":
console.log("Upgrading contract");
break;
case "RegisterChain":
console.log("Registering chain");
break;
case "Transfer":
console.log("Completing transfer");
break;
default:
impossible(payload);
}
break;
case "TokenBridge":
target_contract = contracts.token_bridge;
execute_msg = {
submit_vaa: {
data: fromUint8Array(vaa),
},
};
switch (payload.type) {
case "ContractUpgrade":
console.log("Upgrading contract");
break;
case "RegisterChain":
console.log("Registering chain");
break;
case "Transfer":
console.log("Completing transfer");
break;
case "AttestMeta":
console.log("Creating wrapped token");
break;
case "TransferWithPayload":
throw Error("Can't complete payload 3 transfer from CLI");
default:
impossible(payload);
break;
}
break;
default:
target_contract = impossible(payload);
execute_msg = impossible(payload);
}
const transaction = new MsgExecuteContract(
wallet.key.accAddress,
target_contract,
execute_msg,
{ axpla: 1700000000000000000 }
);
const feeDenoms = ["axpla"];
// const gasPrices = await axios
// .get("https://dimension-lcd.xpla.dev/v1/txs/gas_prices")
// .then((result) => result.data);
const feeEstimate = await client.tx.estimateFee(
[
{
sequenceNumber: await wallet.sequence(),
publicKey: wallet.key.publicKey,
},
],
{
msgs: [transaction],
memo: "",
feeDenoms,
// gasPrices,
}
);
wallet
.createAndSignTx({
msgs: [transaction],
memo: "",
fee: new Fee(
feeEstimate.gas_limit,
feeEstimate.amount.add(new Coin("axpla", 18))
),
})
.then((tx) => client.tx.broadcast(tx))
.then((result) => {
console.log(result);
console.log(`TX hash: ${result.txhash}`);
});
}