wormchain - contract deploy + test

This commit is contained in:
justinschuldt 2022-12-19 08:23:15 -06:00 committed by Justin Schuldt
parent 1f939ea2dd
commit 220a869b7b
29 changed files with 6090 additions and 110 deletions

View File

@ -253,7 +253,7 @@ jobs:
node-version: "16"
- run: cd clients/js && make test
# Verify wormhole chain unit tests
# Verify wormchain tests
wormchain:
runs-on: ubuntu-20.04
steps:
@ -261,11 +261,14 @@ jobs:
- uses: actions/setup-go@v2
with:
go-version: "1.19.3"
- run: |
- name: make proto -B && make test
run: |
cd wormchain
make proto -B
git diff --name-only --exit-code && echo "✅ Generated proto matches committed proto" || (echo "❌ Generated proto differs from committed proto, run \`make proto -B\` and commit the result" >&2 && exit 1)
make test
- name: deploy-test
run: cd wormchain && make contracts-deploy-test
# Verify go sdk unit tests
sdk_vaa:

2
.gitignore vendored
View File

@ -13,6 +13,8 @@ venv
.env
.env.hex
.env.0x
devnet-consts.json
!/scripts/devnet-consts.json
bigtable-admin.json
bigtable-writer.json
**/cert.pem

View File

@ -37,6 +37,9 @@ ENV NUM_GUARDIANS=$num_guardians
# run guardian-set-init.sh to create env files with the init state for NUM_GUARDIANS
RUN ./scripts/guardian-set-init.sh $NUM_GUARDIANS
# run distribute-devnet-consts.sh to copy devnet-consts.json to chain dirs for use
RUN ./scripts/distribute-devnet-consts.sh
FROM scratch AS const-export
COPY --from=const-build /scripts/.env.0x ethereum/.env
COPY --from=const-build /scripts/.env.hex solana/.env
@ -45,3 +48,6 @@ COPY --from=const-build /scripts/.env.hex cosmwasm/deployment/terra2/tools/.env
COPY --from=const-build /scripts/.env.hex algorand/.env
COPY --from=const-build /scripts/.env.hex near/.env
COPY --from=const-build /scripts/.env.hex aptos/.env
COPY --from=const-build /scripts/.env.hex wormchain/contracts/tools/.env
COPY --from=const-build /scripts/devnet-consts.json wormchain/contracts/tools/

View File

@ -756,6 +756,12 @@ if wormchain:
ignore = ["./wormchain/testing", "./wormchain/ts-sdk", "./wormchain/design", "./wormchain/vue", "./wormchain/build/wormchaind"],
)
docker_build(
ref = "wormchain-deploy",
context = "./wormchain/contracts",
dockerfile = "./cosmwasm/Dockerfile.deploy",
)
def build_wormchain_yaml(yaml_path, num_instances):
wormchain_yaml = read_yaml_stream(yaml_path)
@ -795,6 +801,7 @@ if wormchain:
else:
k8s_yaml_with_ns(wormchain_path)
k8s_resource(
"wormchain",
port_forwards = [
@ -802,7 +809,14 @@ if wormchain:
port_forward(9090, container_port = 9090, name = "GRPC", host = webHost),
port_forward(26659, container_port = 26657, name = "TENDERMINT [:26659]", host = webHost)
],
resource_deps = [],
resource_deps = ["const-gen"],
labels = ["wormchain"],
trigger_mode = trigger_mode,
)
k8s_resource(
"wormchain-deploy",
resource_deps = ["wormchain"],
labels = ["wormchain"],
trigger_mode = trigger_mode,
)
@ -823,7 +837,7 @@ if ibc_relayer:
port_forwards = [
port_forward(7597, name = "HTTPDEBUG [:7597]", host = webHost),
],
resource_deps = ["guardian-validator", "terra2-terrad"],
resource_deps = ["wormchain", "terra2-terrad"],
labels = ["ibc-relayer"],
trigger_mode = trigger_mode,
)

View File

@ -222,8 +222,6 @@ const contract_registrations = {
process.env.REGISTER_TERRA_TOKEN_BRIDGE_VAA,
// NEAR
process.env.REGISTER_NEAR_TOKEN_BRIDGE_VAA,
// Wormhole Chain
process.env.REGISTER_WORMCHAIN_TOKEN_BRIDGE_VAA,
// APTOS
process.env.REGISTER_APTOS_TOKEN_BRIDGE_VAA,
],

View File

@ -59,7 +59,7 @@ spec:
- key: wormchainKey0
path: wormchainKey0
- key: wormchainKey1
path: wormchainKey1
path: wormchainKey1
containers:
- name: guardiand
image: guardiand-image
@ -104,19 +104,19 @@ spec:
- --neonRPC
- ws://eth-devnet:8545
# - --wormchainWS
# - ws://guardian-validator:26657/websocket
# - ws://wormchain:26657/websocket
# - --wormchainLCD
# - http://guardian-validator:1317
# - http://wormchain:1317
# - --wormchainURL
# - guardian-validator:9090
# - wormchain:9090
# - --wormchainKeyPath
# - /tmp/mounted-keys/wormchain/wormchainKey
# - --wormchainKeyPassPhrase
# - test0000
# - --accountantWS
# - http://guardian-validator:26657
# - http://wormchain:26657
# - --accountantContract
# - wormhole1466nf3zuxpya8q9emxukd7vftaf6h4psr0a07srl5zw74zh84yjq4lyjmh
# - wormhole1nc5tatafv6eyq7llkr2gv50ff9e22mnf70qgjlv737ktmt4eswrq0kdhcj
# - --accountantCheckEnabled=true
# - --terraWS
# - ws://terra-terrad:26657/websocket
@ -199,5 +199,5 @@ metadata:
name: node-wormchain-key
type: Opaque
data:
wormchainKey0: LS0tLS1CRUdJTiBURU5ERVJNSU5UIFBSSVZBVEUgS0VZLS0tLS0Ka2RmOiBiY3J5cHQKc2FsdDogNDc2ODc2NkE3OEZEN0ZBQjMwMUJGOTM5MUYwQ0Y2M0YKdHlwZTogc2VjcDI1NmsxCgpkbEZuN1ZqRk02RnJjYkdaVDRWeE5yRlE3SUhQS2RyVVBCRTYraW8yK0w0VFZqcis5emNIQTF3dzNubWtqNVFlCnVSekJWMjQyeUdTc3hNTTJZckI2Q1ZXdzlaWXJJY3JFeks1c0FuST0KPXB2aHkKLS0tLS1FTkQgVEVOREVSTUlOVCBQUklWQVRFIEtFWS0tLS0t
wormchainKey0: LS0tLS1CRUdJTiBURU5ERVJNSU5UIFBSSVZBVEUgS0VZLS0tLS0Ka2RmOiBiY3J5cHQKc2FsdDogNDc2ODc2NkE3OEZEN0ZBQjMwMUJGOTM5MUYwQ0Y2M0YKdHlwZTogc2VjcDI1NmsxCgpkbEZuN1ZqRk02RnJjYkdaVDRWeE5yRlE3SUhQS2RyVVBCRTYraW8yK0w0VFZqcis5emNIQTF3dzNubWtqNVFlCnVSekJWMjQyeUdTc3hNTTJZckI2Q1ZXdzlaWXJJY3JFeks1c0FuST0KPXB2aHkKLS0tLS1FTkQgVEVOREVSTUlOVCBQUklWQVRFIEtFWS0tLS0t
wormchainKey1: LS0tLS1CRUdJTiBURU5ERVJNSU5UIFBSSVZBVEUgS0VZLS0tLS0Ka2RmOiBiY3J5cHQKc2FsdDogMjMyRTU2NDMyMjBBNTcwRkVEQjFFMTFFOTNFM0E4NEIKdHlwZTogc2VjcDI1NmsxCgpBZjJ3aXNLdlBDOW4vaExYcDZaS1k5S091aVNYZG1lb3VvSzd3QVJ3cmNtTDV3MGs0YjFDSE5xTEp3ZXU1OEFGCkdTWGJsU3oySzNuWEl1V2hJZWtSNXE5WGRuUko4cGhSRWltbFNZST0KPU1vY1QKLS0tLS1FTkQgVEVOREVSTUlOVCBQUklWQVRFIEtFWS0tLS0tCg==

View File

@ -90,5 +90,40 @@ spec:
httpGet:
port: 26657
path: /
periodSeconds: 1
restartPolicy: Always
---
apiVersion: apps/v1
kind: StatefulSet
metadata:
labels:
app: wormchain-deploy
name: wormchain-deploy
spec:
selector:
matchLabels:
app: wormchain-deploy
replicas: 1
updateStrategy:
type: RollingUpdate
template:
metadata:
labels:
app: wormchain-deploy
spec:
containers:
- name: wormchain-deploy
image: wormchain-deploy
command:
- /bin/sh
- -c
- "npm run deploy-wormchain --prefix=/app/tools && touch /app/tools/success && sleep infinity"
readinessProbe:
exec:
command:
- test
- -e
- "/app/tools/success"
initialDelaySeconds: 5
periodSeconds: 5
restartPolicy: Always

View File

@ -14,7 +14,6 @@ const terra2TokenBridgeVAA = process.env.REGISTER_TERRA2_TOKEN_BRIDGE_VAA;
const bscTokenBridgeVAA = process.env.REGISTER_BSC_TOKEN_BRIDGE_VAA;
const algoTokenBridgeVAA = process.env.REGISTER_ALGO_TOKEN_BRIDGE_VAA;
const nearTokenBridgeVAA = process.env.REGISTER_NEAR_TOKEN_BRIDGE_VAA;
const wormchainTokenBridgeVAA = process.env.REGISTER_WORMCHAIN_TOKEN_BRIDGE_VAA;
const aptosTokenBridgeVAA = process.env.REGISTER_APTOS_TOKEN_BRIDGE_VAA;
module.exports = async function(callback) {
@ -91,16 +90,6 @@ module.exports = async function(callback) {
gasLimit: 2000000,
});
// Register the wormhole token bridge endpoint
console.log("Registering Wormchain...");
await tokenBridge.methods
.registerChain("0x" + wormchainTokenBridgeVAA)
.send({
value: 0,
from: accounts[0],
gasLimit: 2000000,
});
// Register the APTOS endpoint
console.log("Registering Aptos...");
await tokenBridge.methods.registerChain("0x" + aptosTokenBridgeVAA).send({

View File

@ -34,7 +34,7 @@ func main() {
wormchainURL := string("localhost:9090")
wormchainKeyPath := string("./dev.wormchain.key")
contract := "wormhole1466nf3zuxpya8q9emxukd7vftaf6h4psr0a07srl5zw74zh84yjq4lyjmh"
contract := "wormhole1nc5tatafv6eyq7llkr2gv50ff9e22mnf70qgjlv737ktmt4eswrq0kdhcj"
guardianKeyPath := string("./dev.guardian.key")
wormchainKey, err := wormconn.LoadWormchainPrivKey(wormchainKeyPath, "test0000")

View File

@ -238,12 +238,61 @@
"3104": {
"rpcUrlTilt": "http://wormchain:1317",
"rpcUrlLocal": "http://localhost:1319",
"rpcPort": "1319",
"tendermintUrlTilt": "http://wormchain:26657",
"tendermintUrlLocal": "http://localhost:26659",
"rpcPort": "1317",
"tendermintPort": "26657",
"contracts": {
"coreEmitterAddress": "wormhole1ap5vgur5zlgys8whugfegnn43emka567dtq0jl",
"coreNativeAddress": "wormhole1ap5vgur5zlgys8whugfegnn43emka567dtq0jl",
"tokenBridgeEmitterAddress": "wormhole1zugu6cajc4z7ue29g9wnes9a5ep9cs7yu7rn3z",
"tokenBridgeNativeAddress": "wormhole1zugu6cajc4z7ue29g9wnes9a5ep9cs7yu7rn3z"
"accountingNativeAddress": "wormhole1nc5tatafv6eyq7llkr2gv50ff9e22mnf70qgjlv737ktmt4eswrq0kdhcj"
},
"accounts": {
"wormchainNodeOfGuardian0": {
"address": "C10820983F33456CE7BEB3A046F5A83FA34F027D",
"addressBase64": "wQggmD8zRWznvrOgRvWoP6NPAn0=",
"addressWormhole": "000000000000000000000000c10820983f33456ce7beb3a046f5a83fa34f027d",
"public": "wormhole1cyyzpxplxdzkeea7kwsydadg87357qna3zg3tq",
"privateHex": "48d23cc417a30674e907a2403f109f082d92e197823d02e6a423c6aeb8e41204",
"cosmos.crypto.secp256k1.PubKey": "AuwYyCUBxQiBGSUWebU46c+OrlApVsyGLHd4qhSDZeiG",
"tendermint/PrivKeyEd25519": "DONGe0wxovG1ZuCQ1iMbyBCW/hG5UeKz6ZFfhdZYznRSC48Lc1nwhUwXzHtXfwAOY0mO3mhTy4CMwPeYFvBZ1A==",
"mnemonic": "notice oak worry limit wrap speak medal online prefer cluster roof addict wrist behave treat actual wasp year salad speed social layer crew genius"
},
"wormchainValidator0": {
"public": "wormholevaloper1cyyzpxplxdzkeea7kwsydadg87357qna87hzv8",
"tendermint/PubKeyEd25519": "fnfoo/C+i+Ng1J8vct6wfvrTS9JeNIG5UeO87ZHKMkY=",
"tendermint/PrivKeyEd25519": "Zb3gQZSd8qNMyXUQdKmeqM/SSYeVDD80S4XPEsCAgPN+d+ij8L6L42DUny9y3rB++tNL0l40gblR47ztkcoyRg=="
},
"wormchainNodeOfGuardian1": {
"address": "701C475B19A3F68D3FDEBF09591487FACEF2D636",
"addressWormhole": "000000000000000000000000701c475b19a3f68d3fdebf09591487facef2d636",
"addressBase64": "cBxHWxmj9o0/3r8JWRSH+s7y1jY=",
"public": "wormhole1wqwywkce50mg6077huy4j9y8lt80943ks5udzr",
"privateHex": "7095b73fa951fd117d54f3bca130b8088625db2d60d94d4f064791dc1a792b29",
"cosmos.crypto.secp256k1.PubKey": "ApJi/CY2RGyzA5cQtDwU9c+o7T8OE+SjrgcG5PwLMjTP",
"tendermint/PrivKeyEd25519": "TTdzb3XLJbSXP/5VhzPJCWysCDDH2hEXTqdvLI6RYk7rxPwzCXTprp2ZEfSCfQswYgUUQgO9JKzbAtfyeK2G1A==",
"mnemonic": "maple pudding enjoy pole real rabbit soft make square city wrestle area aisle dwarf spike voice over still post lend genius bitter exit shoot"
},
"wormchainValidator1": {
"public": "wormholevaloper1wqwywkce50mg6077huy4j9y8lt80943kxgr79y",
"tendermint/PubKeyEd25519": "Zcujkt1sXRWWLfhgxLAm/Q+ioLn4wFim0OnGPLlCG0I=",
"tendermint/PrivKeyEd25519": "SGWIYI3BgC/dxNOk1gYx6LpChAKqWGtAfZSx0SDFWuhly6OS3WxdFZYt+GDEsCb9D6KgufjAWKbQ6cY8uUIbQg=="
}
},
"addresses": {
"native": {
"address": "uworm",
"addressWormhole": "010c0ded78f1b69ec7b79b9ee592fbbcacebc97db1c695220a833135bfa74824",
"denom": "uworm",
"name": "worm",
"symbol": "worm",
"decimals": 0
},
"testToken": {
"address": "wormhole1zwv6feuzhy6a9wekh96cd57lsarmqlwxdypdsplw6zhfncqw6ftqhnev3f",
"addressWormhole": "003f822e9066cfea09b9ce1247e8f79a86a24dda2d8b3d76a608ae7583220411",
"name": "MOCK",
"symbol": "MCK",
"decimals": 6
}
}
},
"22": {

View File

@ -0,0 +1,20 @@
#!/usr/bin/env bash
set -euo pipefail
# copy devnet-consts.json to chain dirs for local use, so we can keep docker
# build contexts scoped to the chain, rather than the root just to read this file.
file="./scripts/devnet-consts.json"
paths=(
./terra2/tools/
./wormchain/contracts/tools/
)
for dest in "${paths[@]}"; do
dirname=$(dirname $dest)
if [[ -d "$dirname" ]]; then
echo "copying $file to $dest"
cp $file $dest
fi
done
echo "distribute devnet consts complete!"

View File

@ -90,7 +90,6 @@ bscTokenBridge=$(jq --raw-output '.chains."4".contracts.tokenBridgeEmitterAddres
algoTokenBridge=$(jq --raw-output '.chains."8".contracts.tokenBridgeEmitterAddress' $addressesJson)
nearTokenBridge=$(jq --raw-output '.chains."15".contracts.tokenBridgeEmitterAddress' $addressesJson)
terra2TokenBridge=$(jq --raw-output '.chains."18".contracts.tokenBridgeEmitterAddress' $addressesJson)
wormchainTokenBridge=$(jq --raw-output '.chains."3104".contracts.tokenBridgeEmitterAddress' $addressesJson)
aptosTokenBridge=$(jq --raw-output '.chains."22".contracts.tokenBridgeEmitterAddress' $addressesJson)
solNFTBridge=$(jq --raw-output '.chains."1".contracts.nftBridgeEmitterAddress' $addressesJson)
@ -108,7 +107,6 @@ bscTokenBridgeVAA=$(node ./clients/js/build/main.js generate registration -m Tok
algoTokenBridgeVAA=$(node ./clients/js/build/main.js generate registration -m TokenBridge -c algorand -a ${algoTokenBridge} -g ${guardiansPrivateCSV})
nearTokenBridgeVAA=$(node ./clients/js/build/main.js generate registration -m TokenBridge -c near -a ${nearTokenBridge} -g ${guardiansPrivateCSV})
terra2TokenBridgeVAA=$(node ./clients/js/build/main.js generate registration -m TokenBridge -c terra2 -a ${terra2TokenBridge} -g ${guardiansPrivateCSV})
wormchainTokenBridgeVAA=$(node ./clients/js/build/main.js generate registration -m TokenBridge -c wormchain -a ${wormchainTokenBridge} -g ${guardiansPrivateCSV})
aptosTokenBridgeVAA=$(node ./clients/js/build/main.js generate registration -m TokenBridge -c aptos -a ${aptosTokenBridge} -g ${guardiansPrivateCSV})
@ -131,7 +129,6 @@ bscTokenBridge="REGISTER_BSC_TOKEN_BRIDGE_VAA"
algoTokenBridge="REGISTER_ALGO_TOKEN_BRIDGE_VAA"
terra2TokenBridge="REGISTER_TERRA2_TOKEN_BRIDGE_VAA"
nearTokenBridge="REGISTER_NEAR_TOKEN_BRIDGE_VAA"
wormchainTokenBridge="REGISTER_WORMCHAIN_TOKEN_BRIDGE_VAA"
aptosTokenBridge="REGISTER_APTOS_TOKEN_BRIDGE_VAA"
solNFTBridge="REGISTER_SOL_NFT_BRIDGE_VAA"
@ -193,10 +190,6 @@ upsert_env_file $envFile $nearTokenBridge $nearTokenBridgeVAA
upsert_env_file $ethFile $nearNFTBridge $nearNFTBridgeVAA
upsert_env_file $envFile $nearNFTBridge $nearNFTBridgeVAA
# wormchain token bridge
upsert_env_file $ethFile $wormchainTokenBridge $wormchainTokenBridgeVAA
upsert_env_file $envFile $wormchainTokenBridge $wormchainTokenBridgeVAA
# 7) copy the local .env file to the solana & terra dirs, if the script is running on the host machine
# chain dirs will not exist if running in docker for Tilt, only if running locally. check before copying.
# copy ethFile to ethereum
@ -212,6 +205,7 @@ paths=(
./solana/.env
./terra/tools/.env
./cosmwasm/deployment/terra2/tools/.env
./wormchain/contracts/tools/.env
)
for envDest in "${paths[@]}"; do

View File

@ -538,8 +538,8 @@ const DEVNET = {
nft_bridge: undefined,
},
wormchain: {
core: "wormhole1ap5vgur5zlgys8whugfegnn43emka567dtq0jl",
token_bridge: "wormhole1zugu6cajc4z7ue29g9wnes9a5ep9cs7yu7rn3z",
core: "wormhole14hj2tavq8fpesdwxxcu44rty3hh90vhujrvcmstl4zr3txmfvw9srrg465",
token_bridge: "wormhole1nc5tatafv6eyq7llkr2gv50ff9e22mnf70qgjlv737ktmt4eswrq0kdhcj",
nft_bridge: undefined,
},
};

View File

@ -274,8 +274,6 @@ const contract_registrations = {
process.env.REGISTER_TERRA2_TOKEN_BRIDGE_VAA,
// NEAR
process.env.REGISTER_NEAR_TOKEN_BRIDGE_VAA,
// Wormhole Chain
process.env.REGISTER_WORMCHAIN_TOKEN_BRIDGE_VAA,
// APTOS
process.env.REGISTER_APTOS_TOKEN_BRIDGE_VAA,
],

View File

@ -14,6 +14,7 @@ devnet/wormchain-*/config/addrbook.json
devnet/wormchain-*/config/genesis.json
ts-sdk/node_modules
ts-sdk/lib
contracts/artifacts
.idea
*.iml

View File

@ -43,6 +43,31 @@ ts-sdk: vue
run: build/wormchaind
./$< start --home build --log_level="debug"
# get npm packages for contracts/tools
contracts-tools-deps: contracts/tools/package-lock.json
npm ci --prefix=contracts/tools
# get .env and devnet-consts.json for contracts/tools
contracts-devnet-env:
cd .. && ./scripts/guardian-set-init.sh 1
cd .. && ./scripts/distribute-devnet-consts.sh
# get wasm artifacts for cosmwasm contracts
contracts-artifacts:
cd ../cosmwasm && $(MAKE) artifacts
cp -r ../cosmwasm/artifacts contracts
# get everything needed to
contracts-deploy-setup: contracts-tools-deps contracts-devnet-env contracts-artifacts
# runs the contract deployment script
contracts-deploy-local: contracts-deploy-setup
npm run deploy-wormchain --prefix=contracts/tools
# starts wormchaind in a container, deploys contracts, tests contract interaction
contracts-deploy-test: client contracts-deploy-setup
./contracts/run_deploy_test.sh
.PHONY: test
test:
go test -v ./...

View File

@ -1,58 +1,208 @@
halt-height = 0
halt-time = 0
index-events = []
inter-block-cache = true
min-retain-blocks = 0
minimum-gas-prices = "0stake"
pruning = "default"
pruning-interval = "0"
pruning-keep-every = "0"
pruning-keep-recent = "0"
# Cosmos Node config
###############################################################################
### Base Configuration ###
###############################################################################
chain-id = "wormchain"
[api]
address = "tcp://localhost:1317"
enable = true
enabled-unsafe-cors = true
max-open-connections = 1000
rpc-max-body-bytes = 1000000
rpc-read-timeout = 10
rpc-write-timeout = 0
swagger = false
# The minimum gas prices a validator is willing to accept for processing a
# transaction. A transaction's fees must meet the minimum of any denomination
# specified in this config (e.g. 0.25token1;0.0001token2).
minimum-gas-prices = "0uworm"
[grpc]
address = "0.0.0.0:9090"
enable = true
# default: the last 100 states are kept in addition to every 500th state; pruning at 10 block intervals
# nothing: all historic states will be saved, nothing will be deleted (i.e. archiving node)
# everything: all saved states will be deleted, storing only the current and previous state; pruning at 10 block intervals
# custom: allow pruning options to be manually specified through 'pruning-keep-recent', 'pruning-keep-every', and 'pruning-interval'
pruning = "default"
[grpc-web]
address = "0.0.0.0:9091"
enable = true
enable-unsafe-cors = false
# These are applied if and only if the pruning strategy is custom.
pruning-keep-recent = "0"
pruning-keep-every = "0"
pruning-interval = "0"
[rosetta]
address = ":8080"
blockchain = "app"
enable = false
network = "network"
offline = false
retries = 3
# HaltHeight contains a non-zero block height at which a node will gracefully
# halt and shutdown that can be used to assist upgrades and testing.
#
# Note: Commitment of state will be attempted on the corresponding block.
halt-height = 0
[rpc]
cors_allowed_origins = ["*"]
# HaltTime contains a non-zero minimum block time (in Unix seconds) at which
# a node will gracefully halt and shutdown that can be used to assist upgrades
# and testing.
#
# Note: Commitment of state will be attempted on the corresponding block.
halt-time = 0
[state-sync]
snapshot-interval = 0
snapshot-keep-recent = 2
# MinRetainBlocks defines the minimum block height offset from the current
# block being committed, such that all blocks past this offset are pruned
# from Tendermint. It is used as part of the process of determining the
# ResponseCommit.RetainHeight value during ABCI Commit. A value of 0 indicates
# that no blocks should be pruned.
#
# This configuration value is only responsible for pruning Tendermint blocks.
# It has no bearing on application state pruning which is determined by the
# "pruning-*" configurations.
#
# Note: Tendermint block pruning is dependant on this parameter in conunction
# with the unbonding (safety threshold) period, state pruning and state sync
# snapshot parameters to determine the correct minimum value of
# ResponseCommit.RetainHeight.
min-retain-blocks = 0
# InterBlockCache enables inter-block caching.
inter-block-cache = true
# IndexEvents defines the set of events in the form {eventType}.{attributeKey},
# which informs Tendermint what to index. If empty, all events will be indexed.
#
# Example:
# ["message.sender", "message.recipient"]
index-events = []
# IavlCacheSize set the size of the iavl tree cache.
# Default cache size is 50mb.
iavl-cache-size = 781250
# IAVLDisableFastNode enables or disables the fast node feature of IAVL.
# Default is true.
iavl-disable-fastnode = true
###############################################################################
### Telemetry Configuration ###
###############################################################################
[telemetry]
enable-hostname = false
enable-hostname-label = false
enable-service-label = false
enabled = false
global-labels = []
prometheus-retention-time = 0
service-name = ""
# Prefixed with keys to separate services.
service-name = ""
# Enabled enables the application telemetry functionality. When enabled,
# an in-memory sink is also enabled by default. Operators may also enabled
# other sinks such as Prometheus.
enabled = false
# Enable prefixing gauge values with hostname.
enable-hostname = false
# Enable adding hostname to labels.
enable-hostname-label = false
# Enable adding service to labels.
enable-service-label = false
# PrometheusRetentionTime, when positive, enables a Prometheus metrics sink.
prometheus-retention-time = 0
# GlobalLabels defines a global set of name/value label tuples applied to all
# metrics emitted using the wrapper functions defined in telemetry package.
#
# Example:
# [["chain_id", "cosmoshub-1"]]
global-labels = [
]
###############################################################################
### API Configuration ###
###############################################################################
[api]
# Enable defines if the API server should be enabled.
enable = true
# Swagger defines if swagger documentation should automatically be registered.
swagger = false
# Address defines the API server to listen on.
address = "tcp://0.0.0.0:1317"
# MaxOpenConnections defines the number of maximum open connections.
max-open-connections = 1000
# RPCReadTimeout defines the Tendermint RPC read timeout (in seconds).
rpc-read-timeout = 10
# RPCWriteTimeout defines the Tendermint RPC write timeout (in seconds).
rpc-write-timeout = 0
# RPCMaxBodyBytes defines the Tendermint maximum response body (in bytes).
rpc-max-body-bytes = 1000000
# EnableUnsafeCORS defines if CORS should be enabled (unsafe - use it at your own risk).
enabled-unsafe-cors = true
###############################################################################
### Rosetta Configuration ###
###############################################################################
[rosetta]
# Enable defines if the Rosetta API server should be enabled.
enable = false
# Address defines the Rosetta API server to listen on.
address = ":8080"
# Network defines the name of the blockchain that will be returned by Rosetta.
blockchain = "app"
# Network defines the name of the network that will be returned by Rosetta.
network = "network"
# Retries defines the number of retries when connecting to the node before failing.
retries = 3
# Offline defines if Rosetta server should run in offline mode.
offline = false
###############################################################################
### gRPC Configuration ###
###############################################################################
[grpc]
# Enable defines if the gRPC server should be enabled.
enable = true
# Address defines the gRPC server address to bind to.
address = "0.0.0.0:9090"
###############################################################################
### gRPC Web Configuration ###
###############################################################################
[grpc-web]
# GRPCWebEnable defines if the gRPC-web should be enabled.
# NOTE: gRPC must also be enabled, otherwise, this configuration is a no-op.
enable = true
# Address defines the gRPC-web server address to bind to.
address = "0.0.0.0:9091"
# EnableUnsafeCORS defines if CORS should be enabled (unsafe - use it at your own risk).
enable-unsafe-cors = true
###############################################################################
### State Sync Configuration ###
###############################################################################
# State sync snapshots allow other nodes to rapidly join the network without replaying historical
# blocks, instead downloading and applying a snapshot of the application state at a given height.
[state-sync]
# snapshot-interval specifies the block interval at which local state sync snapshots are
# taken (0 to disable). Must be a multiple of pruning-keep-every.
snapshot-interval = 0
# snapshot-keep-recent specifies the number of recent snapshots to keep and serve (0 to keep all).
snapshot-keep-recent = 2
[wasm]
lru_size = 0
query_gas_limit = 300000
# This is the maximum sdk gas (wasm and storage) that we allow for any x/wasm "smart" queries
query_gas_limit = 300000
# This is the number of wasm vm instances we keep cached in memory for speed-up
# Warning: this is currently unstable and may lead to crashes, best to keep for 0 unless testing locally
lru_size = 0

View File

@ -1,5 +1,16 @@
broadcast-mode = "block"
# Tendermint client config
###############################################################################
### Client Configuration ###
###############################################################################
# The network chain ID
chain-id = "wormchain"
# The keyring's backend, where the keys are stored (os|file|kwallet|pass|test|memory)
keyring-backend = "test"
node = "tcp://localhost:26657"
# CLI output format (text|json)
output = "text"
# <host>:<port> to Tendermint RPC interface for this chain
node = "tcp://0.0.0.0:26657"
# Transaction broadcasting mode (sync|async|block)
broadcast-mode = "block"

View File

@ -15,7 +15,7 @@
proxy_app = "tcp://127.0.0.1:26658"
# A custom human readable name for this node
moniker = "mynode"
moniker = "wormchain-local"
# If this node is many blocks behind the tip of the chain, FastSync
# allows them to catchup quickly by downloading blocks in parallel
@ -93,7 +93,7 @@ laddr = "tcp://0.0.0.0:26657"
# A list of origins a cross-domain request can be executed from
# Default value '[]' disables cors support
# Use '["*"]' to allow any origin
cors_allowed_origins = ["*", ]
cors_allowed_origins = []
# A list of methods the client is allowed to use with cross-domain requests
cors_allowed_methods = ["HEAD", "GET", "POST", ]
@ -136,6 +136,33 @@ max_subscription_clients = 100
# the estimated # maximum number of broadcast_tx_commit calls per block.
max_subscriptions_per_client = 5
# Experimental parameter to specify the maximum number of events a node will
# buffer, per subscription, before returning an error and closing the
# subscription. Must be set to at least 100, but higher values will accommodate
# higher event throughput rates (and will use more memory).
experimental_subscription_buffer_size = 200
# Experimental parameter to specify the maximum number of RPC responses that
# can be buffered per WebSocket client. If clients cannot read from the
# WebSocket endpoint fast enough, they will be disconnected, so increasing this
# parameter may reduce the chances of them being disconnected (but will cause
# the node to use more memory).
#
# Must be at least the same as "experimental_subscription_buffer_size",
# otherwise connections could be dropped unnecessarily. This value should
# ideally be somewhat higher than "experimental_subscription_buffer_size" to
# accommodate non-subscription-related RPC responses.
experimental_websocket_write_buffer_size = 200
# If a WebSocket client cannot read fast enough, at present we may
# silently drop events instead of generating an error or disconnecting the
# client.
#
# Enabling this experimental parameter will cause the WebSocket connection to
# be closed instead if it cannot read fast enough, allowing for greater
# predictability in subscription behaviour.
experimental_close_on_slow_client = false
# How long to wait for a tx to be committed during /broadcast_tx_commit.
# WARNING: Using a value larger than 10s will result in increasing the
# global HTTP write timeout, which applies to all connections and endpoints.
@ -245,6 +272,11 @@ dial_timeout = "3s"
#######################################################
[mempool]
# Mempool version to use:
# 1) "v0" - (default) FIFO mempool.
# 2) "v1" - prioritized mempool.
version = "v0"
recheck = true
broadcast = true
wal_dir = ""
@ -274,6 +306,22 @@ max_tx_bytes = 1048576
# XXX: Unused due to https://github.com/tendermint/tendermint/issues/5796
max_batch_bytes = 0
# ttl-duration, if non-zero, defines the maximum amount of time a transaction
# can exist for in the mempool.
#
# Note, if ttl-num-blocks is also defined, a transaction will be removed if it
# has existed in the mempool at least ttl-num-blocks number of blocks or if it's
# insertion time into the mempool is beyond ttl-duration.
ttl-duration = "0s"
# ttl-num-blocks, if non-zero, defines the maximum number of blocks a transaction
# can exist for in the mempool.
#
# Note, if ttl-duration is also defined, a transaction will be removed if it
# has existed in the mempool at least ttl-num-blocks number of blocks or if
# it's insertion time into the mempool is beyond ttl-duration.
ttl-num-blocks = 0
#######################################################
### State Sync Configuration Options ###
#######################################################
@ -362,6 +410,16 @@ create_empty_blocks_interval = "0s"
peer_gossip_sleep_duration = "100ms"
peer_query_maj23_sleep_duration = "2s"
#######################################################
### Storage Configuration Options ###
#######################################################
# Set to true to discard ABCI responses from the state store, which can save a
# considerable amount of disk space. Set to false to ensure ABCI responses are
# persisted. ABCI responses are required for /block_results RPC queries, and to
# reindex events in the command-line tool.
discard_abci_responses = false
#######################################################
### Transaction Indexer Configuration Options ###
#######################################################
@ -376,8 +434,14 @@ peer_query_maj23_sleep_duration = "2s"
# 1) "null"
# 2) "kv" (default) - the simplest possible indexer, backed by key-value storage (defaults to levelDB; see DBBackend).
# - When "kv" is chosen "tx.height" and "tx.hash" will always be indexed.
# 3) "psql" - the indexer services backed by PostgreSQL.
# When "kv" or "psql" is chosen "tx.height" and "tx.hash" will always be indexed.
indexer = "kv"
# The PostgreSQL connection configuration, the connection format:
# postgresql://<user>:<password>@<host>:<port>/<db>?<opts>
psql-conn = ""
#######################################################
### Instrumentation Configuration Options ###
#######################################################

View File

@ -65,7 +65,11 @@
"coins": [
{
"denom": "utest",
"amount": "10000"
"amount": "100000000"
},
{
"denom": "uworm",
"amount": "1000000000"
}
]
},
@ -74,7 +78,11 @@
"coins": [
{
"denom": "utest",
"amount": "20000"
"amount": "100000000000"
},
{
"denom": "uworm",
"amount": "200000000"
}
]
},
@ -380,4 +388,4 @@
"sequenceCounterList": []
}
}
}
}

View File

@ -0,0 +1,29 @@
#!/usr/bin/env bash
set -exu pipefail
echo "Starting wormchaind"
./build/wormchaind start --home build --rpc.laddr="tcp://0.0.0.0:26659" > wormchaind.out 2>&1 &
# give wormchain 2 seconds to startup
sleep 2
cleanup() {
echo "cleaning up test container"
# kill wormchain, any dependents of the process (just in case)
pkill -f "./build/wormchaind start --home build --rpc.laddr=tcp://0.0.0.0:26659"
pkill -P $$
# remove wormchaind log file
rm wormchaind.out
}
cleanup_and_exit_failure() {
cleanup
echo "exiting with failure code"
exit 1
}
# run the deploy, and catch if it returns an error code
npm run deploy-and-test --prefix contracts/tools || cleanup_and_exit_failure
# cleanup and return success
cleanup

View File

@ -0,0 +1 @@
save-exact=true

View File

@ -0,0 +1,206 @@
import "dotenv/config";
import * as os from "os"
import { SigningCosmWasmClient, InstantiateResult } from "@cosmjs/cosmwasm-stargate";
import { GasPrice } from "@cosmjs/stargate"
import { Secp256k1HdWallet } from "@cosmjs/amino";
import { MsgExecuteContract } from "cosmjs-types/cosmwasm/wasm/v1/tx";
import * as fs from "fs";
import { readdirSync, } from "fs";
import * as util from 'util'
import { toUtf8 } from "@cosmjs/encoding";
import * as devnetConsts from "./devnet-consts.json"
if (process.env.INIT_SIGNERS === "undefined") {
let msg = `.env is missing. run "make contracts-tools-deps" to fetch.`
console.error(msg)
throw msg
}
const readFileAsync = util.promisify(fs.readFile);
/*
NOTE: Only append to this array: keeping the ordering is crucial, as the
contracts must be imported in a deterministic order so their addresses remain
deterministic.
*/
type ContractName = string
const artifacts: ContractName[] = [
"cw20_base.wasm",
"wormchain_accounting.wasm",
];
const ARTIFACTS_PATH = "../artifacts/"
/* Check that the artifact folder contains all the wasm files we expect and nothing else */
try {
const actual_artifacts = readdirSync(ARTIFACTS_PATH).filter((a) =>
a.endsWith(".wasm")
);
const missing_artifacts = artifacts.filter(
(a) => !actual_artifacts.includes(a)
);
if (missing_artifacts.length) {
console.log(
"Error during wormchain deployment. The following files are expected to be in the artifacts folder:"
);
missing_artifacts.forEach((file) => console.log(` - ${file}`));
console.log(
"Hint: the deploy script needs to run after the contracts have been built."
);
console.log(
"External binary blobs need to be manually added in tools/Dockerfile."
);
process.exit(1);
}
} catch (err) {
console.error(`${ARTIFACTS_PATH} cannot be read. Do you need to run "make contracts-deploy-setup"?`)
process.exit(1)
}
async function main() {
/* Set up cosmos client & wallet */
let host = devnetConsts.chains[3104].tendermintUrlLocal
if (os.hostname().includes("wormchain-deploy")) {
// running in tilt devnet
host = devnetConsts.chains[3104].tendermintUrlTilt
}
const denom = devnetConsts.chains[3104].addresses.native.denom
const mnemonic = devnetConsts.chains[3104].accounts.wormchainNodeOfGuardian0.mnemonic
const addressPrefix = "wormhole"
const w = await Secp256k1HdWallet.fromMnemonic(mnemonic, { prefix: addressPrefix })
const gas = GasPrice.fromString(`0${denom}`)
let cwc: SigningCosmWasmClient
try {
cwc = await SigningCosmWasmClient.connectWithSigner(host, w, { prefix: addressPrefix, gasPrice: gas })
} catch (e) {
let msg = `could not connect to wormchain host: ${host}`
if (e?.message) {
console.error(e.message)
}
throw msg
}
// there are several Cosmos chains in devnet, so check the config is as expected
let id = await cwc.getChainId()
if (id !== "wormchain") {
throw new Error(`Wormchain CosmWasmClient connection produced an unexpected chainID: ${id}`)
}
const signers = await w.getAccounts()
const signer = signers[0].address
console.log("wormchain contract deployer is: ", signer)
/* Deploy artifacts */
const codeIds: { [name: ContractName]: number } = await artifacts.reduce(async (prev, file) => {
// wait for the previous to finish, to avoid the race condition of wallet sequence mismatch.
const accum = await prev
const contract_bytes = await readFileAsync(`${ARTIFACTS_PATH}${file}`);
const i = await cwc.upload(signer, contract_bytes, "auto", "")
console.log(`uploaded ${file}, codeID: ${i.codeId}, tx: ${i.transactionHash}`, i.codeId, i.transactionHash)
accum[file] = i.codeId
return accum
}, Object())
// Instantiate contracts.
async function instantiate(code_id: number, inst_msg: any, label: string) {
let inst = await cwc.instantiate(signer, code_id, inst_msg, label, "auto", {})
let addr = inst.contractAddress
let txHash = inst.transactionHash
console.log(`deployed contract ${label}, codeID: ${code_id}, address: ${addr}, txHash: ${txHash}`)
return addr
}
// Instantiate contracts.
// NOTE: Only append at the end, the ordering must be deterministic.
const addresses: { [contractName: string]: InstantiateResult["transactionHash"] } = {};
const init_guardians: string[] = JSON.parse(String(process.env.INIT_SIGNERS));
if (!init_guardians || init_guardians.length === 0) {
throw "failed to get initial guardians from .env file.";
}
addresses["mock.wasm"] = await instantiate(
codeIds["cw20_base.wasm"],
{
name: "MOCK",
symbol: "MCK",
decimals: 6,
initial_balances: [
{
address: signer,
amount: "100000000",
},
],
mint: null,
},
"mock"
);
console.log('instantiated cw20 MOCK token: ', addresses["mock.wasm"])
const registrations: { [chainName: string]: string } = {
// keys are only used for logging success/failure
"solana": String(process.env.REGISTER_SOL_TOKEN_BRIDGE_VAA),
"ethereum": String(process.env.REGISTER_ETH_TOKEN_BRIDGE_VAA),
"bsc": String(process.env.REGISTER_BSC_TOKEN_BRIDGE_VAA),
"algo": String(process.env.REGISTER_ALGO_TOKEN_BRIDGE_VAA),
"terra": String(process.env.REGISTER_TERRA_TOKEN_BRIDGE_VAA),
"near": String(process.env.REGISTER_NEAR_TOKEN_BRIDGE_VAA),
"terra2": String(process.env.REGISTER_TERRA2_TOKEN_BRIDGE_VAA),
"aptos": String(process.env.REGISTER_APTOS_TOKEN_BRIDGE_VAA),
}
const instantiateMsg = {}
addresses["wormchain_accounting.wasm"] = await instantiate(
codeIds["wormchain_accounting.wasm"],
instantiateMsg,
"wormchainAccounting"
)
console.log("instantiated accounting: ", addresses["wormchain_accounting.wasm"])
const accountingRegistrations = Object.values(registrations)
.map(r => Buffer.from(r, "hex").toString("base64"))
const msg = {
typeUrl: "/cosmwasm.wasm.v1.MsgExecuteContract",
value: MsgExecuteContract.fromPartial({
sender: signer,
contract: addresses["wormchain_accounting.wasm"],
msg: toUtf8(JSON.stringify({
submit_v_a_as: {
vaas: accountingRegistrations,
}
}))
})
}
const res = await cwc.signAndBroadcast(signer, [msg], "auto");
console.log(`sent accounting chain registrations, tx: `, res.transactionHash);
}
try {
main()
} catch (e: any) {
if (e?.message) {
console.error(e.message)
}
throw e
}

5160
wormchain/contracts/tools/package-lock.json generated Normal file

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,27 @@
{
"name": "@wormhole-foundation/wormchain-contract-tools",
"version": "0.0.1",
"description": "scripts for working with wormchain contracts",
"main": "deploy_wormchain.ts",
"scripts": {
"deploy-wormchain": "ts-node deploy_wormchain.ts",
"test-wormchain": "ts-node test_wormchain.ts",
"deploy-and-test": "npm run deploy-wormchain && npm run test-wormchain"
},
"keywords": [],
"author": "",
"dependencies": {
"@cosmjs/cosmwasm-stargate": "0.29.5",
"cosmwasm": "1.1.1",
"dotenv": "16.0.3",
"elliptic": "6.5.4",
"ethers": "5.7.2",
"web3-eth-abi": "1.8.1",
"yargs": "17.6.2"
},
"devDependencies": {
"@types/elliptic": "6.4.14",
"ts-node": "10.9.1",
"typescript": "4.9.4"
}
}

View File

@ -0,0 +1,166 @@
import "dotenv/config";
import * as os from "os"
import { SigningCosmWasmClient, toBinary } from "@cosmjs/cosmwasm-stargate";
import { GasPrice } from "@cosmjs/stargate"
import { fromBase64 } from "cosmwasm";
import { Secp256k1HdWallet } from "@cosmjs/amino";
import { zeroPad } from "ethers/lib/utils.js";
import { keccak256 } from "@cosmjs/crypto"
import * as elliptic from "elliptic"
import { concatArrays, encodeUint8 } from "./utils";
import * as devnetConsts from "./devnet-consts.json"
function signBinary(key: elliptic.ec.KeyPair, binary: string): Uint8Array {
// base64 string to Uint8Array,
// so we have bytes to work with for signing, though not sure 100% that's correct.
const bytes = fromBase64(binary);
// create the "digest" for signing.
// The contract will calculate the digest of the "data",
// then use that with the signature to ec recover the publickey that signed.
const digest = keccak256(keccak256(bytes));
// sign the digest
const signature = key.sign(digest, { canonical: true });
// create 65 byte signature (64 + 1)
const signedParts = [
zeroPad(signature.r.toBuffer(), 32),
zeroPad(signature.s.toBuffer(), 32),
encodeUint8(signature.recoveryParam || 0),
];
// combine parts to be Uint8Array with length 65
const signed = concatArrays(signedParts);
return signed
}
async function main() {
/* Set up cosmos client & wallet */
const WORMCHAIN_ID = 3104
let host = devnetConsts.chains[3104].tendermintUrlLocal
if (os.hostname().includes("wormchain-deploy")) {
// running in tilt devnet
host = devnetConsts.chains[3104].tendermintUrlTilt
}
const denom = devnetConsts.chains[WORMCHAIN_ID].addresses.native.denom
const mnemonic = devnetConsts.chains[WORMCHAIN_ID].accounts.wormchainNodeOfGuardian0.mnemonic
const addressPrefix = "wormhole"
const signerPk = devnetConsts.devnetGuardians[0].private
const accountingAddress = devnetConsts.chains[WORMCHAIN_ID].contracts.accountingNativeAddress
const w = await Secp256k1HdWallet.fromMnemonic(mnemonic, { prefix: addressPrefix })
const gas = GasPrice.fromString(`0${denom}`)
let cwc = await SigningCosmWasmClient.connectWithSigner(host, w, { prefix: addressPrefix, gasPrice: gas })
// there is no danger here, just several Cosmos chains in devnet, so check for config issues
let id = await cwc.getChainId()
if (id !== "wormchain") {
throw new Error(`Wormchain CosmWasmClient connection produced an unexpected chainID: ${id}`)
}
const signers = await w.getAccounts()
const signer = signers[0].address
console.log("wormchain wallet pubkey: ", signer)
const nativeBalance = await cwc.getBalance(signer, denom)
console.log("nativeBalance ", nativeBalance.amount)
const utestBalance = await cwc.getBalance(signer, "utest")
console.log("utest balance ", utestBalance.amount)
// create key for guardian0
const ec = new elliptic.ec("secp256k1");
// create key from the devnet guardian0's private key
const key = ec.keyFromPrivate(Buffer.from(signerPk, "hex"));
// Test empty observation
// object to json string, then to base64 (serde binary)
const arrayBinaryString = toBinary([]);
// combine parts to be Uint8Array with length 65
const signedEmptyArray = signBinary(key, arrayBinaryString)
const observeEmptyArray = {
submit_observations: {
observations: arrayBinaryString,
guardian_set_index: 0,
signature: {
index: 0,
signature: Array.from(signedEmptyArray),
},
},
};
let emptyArrayObsRes = await cwc.execute(signer, accountingAddress, observeEmptyArray, "auto");
console.log(`emptyArrayObsRes.transactionHash: ${emptyArrayObsRes.transactionHash}`);
// Test (fake) observation
const emitter_address = "0000000000000000000000000290fb167208af455bb137780163b7b7a9a10c16"
const observations = [
{
emitter_chain: 2,
emitter_address: emitter_address,
sequence: 2,
nonce: 1,
consistency_level: 0,
timestamp: 1,
payload:
Buffer.from("030000000000000000000000000000000000000000000000000000000005f5e1000000000000000000000000002d8be6bf0baa74e0a907016679cae9190e80dd0a0002000000000000000000000000c10820983f33456ce7beb3a046f5a83fa34f027d0c2000000000000000000000000000000000000000000000000000000000000f4240", "hex").toString("base64"),
tx_hash:
Buffer.from("9fc68fb0ee735d45c9074a20adef1747b0593803f33b9f3f2252c8e2df567f41", "hex").toString("base64")
},
];
// object to json string, then to base64 (serde binary)
const observationsBinaryString = toBinary(observations);
const signed = signBinary(key, observationsBinaryString)
const executeMsg = {
submit_observations: {
observations: observationsBinaryString,
guardian_set_index: 0,
signature: {
index: 0,
signature: Array.from(signed),
},
},
};
console.log(executeMsg);
let inst = await cwc.execute(
signer,
accountingAddress,
executeMsg,
"auto"
);
let txHash = inst.transactionHash;
console.log(`executed submit_observation! txHash: ${txHash}`);
console.log("done, exiting success.")
}
try {
main()
} catch (e) {
console.error(e)
throw e
}

View File

@ -0,0 +1,5 @@
{
"compilerOptions": {
"resolveJsonModule": true
}
}

View File

@ -0,0 +1,19 @@
export function concatArrays(arrays: Uint8Array[]): Uint8Array {
const totalLength = arrays.reduce((accum, x) => accum + x.length, 0);
const result = new Uint8Array(totalLength);
for (let i = 0, offset = 0; i < arrays.length; i++) {
result.set(arrays[i], offset);
offset += arrays[i].length;
}
return result;
}
export function encodeUint8(value: number): Uint8Array {
if (value >= 2 ** 8 || value < 0) {
throw new Error(`Out of bound value in Uint8: ${value}`);
}
return new Uint8Array([value]);
}

View File

@ -1,16 +1,16 @@
{
"type": "cosmos",
"value": {
"key": "default",
"chain-id": "wormchain",
"rpc-addr": "http://guardian-validator:26657",
"account-prefix": "wormhole",
"keyring-backend": "test",
"gas-adjustment": 1.2,
"gas-prices": "0.01utest",
"debug": true,
"timeout": "20s",
"output-format": "json",
"sign-mode": "direct"
}
}
"type": "cosmos",
"value": {
"key": "default",
"chain-id": "wormchain",
"rpc-addr": "http://wormchain:26657",
"account-prefix": "wormhole",
"keyring-backend": "test",
"gas-adjustment": 1.2,
"gas-prices": "0.01utest",
"debug": true,
"timeout": "20s",
"output-format": "json",
"sign-mode": "direct"
}
}