initial terra contracts for wormhole v2
Change-Id: Ie28f1e7ce381fcbf33de609bc6c8465679cd2a43
This commit is contained in:
parent
e1c2f73e29
commit
16cc520187
|
@ -2,11 +2,11 @@
|
|||
# It is not intended for manual editing.
|
||||
[[package]]
|
||||
name = "addr2line"
|
||||
version = "0.14.1"
|
||||
version = "0.15.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a55f82cfe485775d02112886f4169bde0c5894d75e79ead7eafe7e40a25e45f7"
|
||||
checksum = "03345e98af8f3d786b6d9f656ccfa6ac316d954e92bc4841f0bba20789d5fb5a"
|
||||
dependencies = [
|
||||
"gimli 0.23.0",
|
||||
"gimli 0.24.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
@ -35,11 +35,12 @@ checksum = "cdb031dd78e28731d87d56cc8ffef4a8f36ca26c38fe2de700543e627f8a464a"
|
|||
|
||||
[[package]]
|
||||
name = "backtrace"
|
||||
version = "0.3.56"
|
||||
version = "0.3.59"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9d117600f438b1707d4e4ae15d3595657288f8235a0eb593e80ecc98ab34e1bc"
|
||||
checksum = "4717cfcbfaa661a0fd48f8453951837ae7e8f81e481fbb136e3202d72805a744"
|
||||
dependencies = [
|
||||
"addr2line",
|
||||
"cc",
|
||||
"cfg-if 1.0.0",
|
||||
"libc",
|
||||
"miniz_oxide",
|
||||
|
@ -54,12 +55,21 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
|||
checksum = "b41b7ea54a0c9d92199de89e20e58d49f02f8e699814ef3fdf266f6f748d15c7"
|
||||
|
||||
[[package]]
|
||||
name = "bincode"
|
||||
version = "1.3.1"
|
||||
name = "bigint"
|
||||
version = "4.4.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f30d3a39baa26f9651f17b375061f3233dde33424a8b72b0dbe93a68a0bc896d"
|
||||
checksum = "c0e8c8a600052b52482eff2cf4d810e462fdff1f656ac1ecb6232132a1ed7def"
|
||||
dependencies = [
|
||||
"byteorder",
|
||||
"crunchy",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "bincode"
|
||||
version = "1.3.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b1f45e9417d87227c7a56d22e471c6206462cba514c7590c09aff4cf6d1ddcad"
|
||||
dependencies = [
|
||||
"serde",
|
||||
]
|
||||
|
||||
|
@ -82,9 +92,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "blake3"
|
||||
version = "0.3.7"
|
||||
version = "0.3.8"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e9ff35b701f3914bdb8fad3368d822c766ef2858b2583198e41639b936f09d3f"
|
||||
checksum = "b64485778c4f16a6a5a9d335e80d449ac6c70cdd6a06d2af18a6f6f775a125b3"
|
||||
dependencies = [
|
||||
"arrayref",
|
||||
"arrayvec",
|
||||
|
@ -119,9 +129,9 @@ checksum = "14c189c53d098945499cdfa7ecc63567cf3886b3332b312a5b4585d8d3a6a610"
|
|||
|
||||
[[package]]
|
||||
name = "cc"
|
||||
version = "1.0.67"
|
||||
version = "1.0.68"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e3c69b077ad434294d3ce9f1f6143a2a4b89a8a2d54ef813d85003a4fd1137fd"
|
||||
checksum = "4a72c244c1ff497a746a7e1fb3d14bd08420ecda70c8f25c7112f2781652d787"
|
||||
|
||||
[[package]]
|
||||
name = "cfg-if"
|
||||
|
@ -194,10 +204,13 @@ dependencies = [
|
|||
]
|
||||
|
||||
[[package]]
|
||||
name = "cpuid-bool"
|
||||
version = "0.1.2"
|
||||
name = "cpufeatures"
|
||||
version = "0.1.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8aebca1129a03dc6dc2b127edd729435bbc4a37e1d5f4d7513165089ceb02634"
|
||||
checksum = "ed00c67cb5d0a7d64a44f6ad2668db7e7530311dd53ea79bcd4fb022c64911c8"
|
||||
dependencies = [
|
||||
"libc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "cranelift-bforest"
|
||||
|
@ -261,9 +274,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "crossbeam-channel"
|
||||
version = "0.5.0"
|
||||
version = "0.5.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "dca26ee1f8d361640700bde38b2c37d8c22b3ce2d360e1fc1c74ea4b0aa7d775"
|
||||
checksum = "06ed27e177f16d65f0f0c22a213e17c696ace5dd64b14258b52f9417ccb52db4"
|
||||
dependencies = [
|
||||
"cfg-if 1.0.0",
|
||||
"crossbeam-utils",
|
||||
|
@ -282,9 +295,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "crossbeam-epoch"
|
||||
version = "0.9.3"
|
||||
version = "0.9.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2584f639eb95fea8c798496315b297cf81b9b58b6d30ab066a75455333cf4b12"
|
||||
checksum = "52fb27eab85b17fbb9f6fd667089e07d6a2eb8743d02639ee7f6a7a7729c9c94"
|
||||
dependencies = [
|
||||
"cfg-if 1.0.0",
|
||||
"crossbeam-utils",
|
||||
|
@ -295,15 +308,21 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "crossbeam-utils"
|
||||
version = "0.8.3"
|
||||
version = "0.8.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e7e9d99fa91428effe99c5c6d4634cdeba32b8cf784fc428a2a687f61a952c49"
|
||||
checksum = "4feb231f0d4d6af81aed15928e58ecf5816aa62a2393e2c82f46973e92a9a278"
|
||||
dependencies = [
|
||||
"autocfg",
|
||||
"cfg-if 1.0.0",
|
||||
"lazy_static",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "crunchy"
|
||||
version = "0.1.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a2f4a431c5c9f662e1200b7c7f02c34e91361150e382089a8f2dec3ba680cbda"
|
||||
|
||||
[[package]]
|
||||
name = "crypto-mac"
|
||||
version = "0.8.0"
|
||||
|
@ -521,9 +540,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "gimli"
|
||||
version = "0.23.0"
|
||||
version = "0.24.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f6503fe142514ca4799d4c26297c4248239fe8838d827db6bd6065c6ed29a6ce"
|
||||
checksum = "0e4075386626662786ddb0ec9081e7c7eeb1ba31951f447ca780ef9f5d568189"
|
||||
|
||||
[[package]]
|
||||
name = "group"
|
||||
|
@ -609,9 +628,9 @@ checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646"
|
|||
|
||||
[[package]]
|
||||
name = "libc"
|
||||
version = "0.2.91"
|
||||
version = "0.2.95"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8916b1f6ca17130ec6568feccee27c156ad12037880833a3b842a823236502e7"
|
||||
checksum = "789da6d93f1b866ffe175afc5322a4d76c038605a1c3319bb57b06967ca98a36"
|
||||
|
||||
[[package]]
|
||||
name = "lock_api"
|
||||
|
@ -643,9 +662,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "memoffset"
|
||||
version = "0.6.1"
|
||||
version = "0.6.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "157b4208e3059a8f9e78d559edc658e13df41410cb3ae03979c83130067fdd87"
|
||||
checksum = "59accc507f1338036a0477ef61afdae33cde60840f4dfe481319ce3ad116ddf9"
|
||||
dependencies = [
|
||||
"autocfg",
|
||||
]
|
||||
|
@ -685,9 +704,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "object"
|
||||
version = "0.23.0"
|
||||
version = "0.24.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a9a7ab5d64814df0fe4a4b5ead45ed6c5f181ee3ff04ba344313a6c80446c5d4"
|
||||
checksum = "1a5b3dd1c072ee7963717671d1ca129f1048fda25edea6b752bfc71ac8854170"
|
||||
|
||||
[[package]]
|
||||
name = "opaque-debug"
|
||||
|
@ -737,9 +756,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "proc-macro2"
|
||||
version = "1.0.24"
|
||||
version = "1.0.27"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1e0704ee1a7e00d7bb417d0770ea303c1bccbabf0ef1667dae92b5967f5f8a71"
|
||||
checksum = "f0d8caf72986c1a598726adc988bb5984792ef84f5ee5aa50209145ee8077038"
|
||||
dependencies = [
|
||||
"unicode-xid",
|
||||
]
|
||||
|
@ -778,9 +797,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "rayon"
|
||||
version = "1.5.0"
|
||||
version = "1.5.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8b0d8e0819fadc20c74ea8373106ead0600e3a67ef1fe8da56e39b9ae7275674"
|
||||
checksum = "c06aca804d41dbc8ba42dfd964f0d01334eceb64314b9ecf7c5fad5188a06d90"
|
||||
dependencies = [
|
||||
"autocfg",
|
||||
"crossbeam-deque",
|
||||
|
@ -790,9 +809,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "rayon-core"
|
||||
version = "1.9.0"
|
||||
version = "1.9.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9ab346ac5921dc62ffa9f89b7a773907511cdfa5490c572ae9be1be33e8afa4a"
|
||||
checksum = "d78120e2c850279833f1dd3582f730c4ab53ed95aeaaaa862a2a5c71b1656d8e"
|
||||
dependencies = [
|
||||
"crossbeam-channel",
|
||||
"crossbeam-deque",
|
||||
|
@ -809,9 +828,9 @@ checksum = "41cc0f7e4d5d4544e8861606a285bb08d3e70712ccc7d2b84d7c0ccfaf4b05ce"
|
|||
|
||||
[[package]]
|
||||
name = "rustc-demangle"
|
||||
version = "0.1.18"
|
||||
version = "0.1.19"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6e3bad0ee36814ca07d7968269dd4b7ec89ec2da10c4bb613928d3077083c232"
|
||||
checksum = "410f7acf3cb3a44527c5d9546bad4bf4e6c460915d5f9f2fc524498bfe8f70ce"
|
||||
|
||||
[[package]]
|
||||
name = "rustc_version"
|
||||
|
@ -874,9 +893,9 @@ checksum = "388a1df253eca08550bef6c72392cfe7c30914bf41df5269b68cbd6ff8f570a3"
|
|||
|
||||
[[package]]
|
||||
name = "serde"
|
||||
version = "1.0.125"
|
||||
version = "1.0.126"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "558dc50e1a5a5fa7112ca2ce4effcb321b0300c0d4ccf0776a9f60cd89031171"
|
||||
checksum = "ec7505abeacaec74ae4778d9d9328fe5a5d04253220a85c4ee022239fc996d03"
|
||||
dependencies = [
|
||||
"serde_derive",
|
||||
]
|
||||
|
@ -911,9 +930,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "serde_derive"
|
||||
version = "1.0.125"
|
||||
version = "1.0.126"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b093b7a2bb58203b5da3056c05b4ec1fed827dcfdb37347a8841695263b3d06d"
|
||||
checksum = "963a7dbc9895aeac7ac90e74f34a5d5261828f79df35cbed41e10189d3804d43"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
|
@ -944,13 +963,13 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "sha2"
|
||||
version = "0.9.3"
|
||||
version = "0.9.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "fa827a14b29ab7f44778d14a88d3cb76e949c45083f7dbfa507d0cb699dc12de"
|
||||
checksum = "b362ae5752fd2137731f9fa25fd4d9058af34666ca1966fb969119cc35719f12"
|
||||
dependencies = [
|
||||
"block-buffer",
|
||||
"cfg-if 1.0.0",
|
||||
"cpuid-bool",
|
||||
"cpufeatures",
|
||||
"digest 0.9.0",
|
||||
"opaque-debug",
|
||||
]
|
||||
|
@ -1013,9 +1032,9 @@ checksum = "1e81da0851ada1f3e9d4312c704aa4f8806f0f9d69faaf8df2f3464b4a9437c2"
|
|||
|
||||
[[package]]
|
||||
name = "syn"
|
||||
version = "1.0.64"
|
||||
version = "1.0.72"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3fd9d1e9976102a03c542daa2eff1b43f9d72306342f3f8b3ed5fb8908195d6f"
|
||||
checksum = "a1e8cdbefb79a9a5a65e0db8b47b723ee907b7c7f8496c76a1770b5c310bab82"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
|
@ -1030,24 +1049,47 @@ checksum = "ab0e7238dcc7b40a7be719a25365910f6807bd864f4cce6b2e6b873658e2b19d"
|
|||
|
||||
[[package]]
|
||||
name = "thiserror"
|
||||
version = "1.0.24"
|
||||
version = "1.0.25"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e0f4a65597094d4483ddaed134f409b2cb7c1beccf25201a9f73c719254fa98e"
|
||||
checksum = "fa6f76457f59514c7eeb4e59d891395fab0b2fd1d40723ae737d64153392e9c6"
|
||||
dependencies = [
|
||||
"thiserror-impl",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "thiserror-impl"
|
||||
version = "1.0.24"
|
||||
version = "1.0.25"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7765189610d8241a44529806d6fd1f2e0a08734313a35d5b3a556f92b381f3c0"
|
||||
checksum = "8a36768c0fbf1bb15eca10defa29526bda730a2376c2ab4393ccfa16fb1a318d"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "token-bridge"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"bigint",
|
||||
"cosmwasm-std",
|
||||
"cosmwasm-storage",
|
||||
"cosmwasm-vm",
|
||||
"cw20",
|
||||
"cw20-base",
|
||||
"cw20-wrapped",
|
||||
"generic-array 0.14.4",
|
||||
"hex",
|
||||
"k256",
|
||||
"lazy_static",
|
||||
"schemars",
|
||||
"serde",
|
||||
"serde_json",
|
||||
"sha3",
|
||||
"thiserror",
|
||||
"wormhole",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "typenum"
|
||||
version = "1.13.0"
|
||||
|
@ -1056,9 +1098,9 @@ checksum = "879f6906492a7cd215bfa4cf595b600146ccfac0c79bcbd1f3000162af5e8b06"
|
|||
|
||||
[[package]]
|
||||
name = "unicode-xid"
|
||||
version = "0.2.1"
|
||||
version = "0.2.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f7fe0bb3479651439c9112f72b6c505038574c9fbb575ed1bf3b797fa39dd564"
|
||||
checksum = "8ccb82d61f80a663efe1f787a51b16b5a51e3314d6ac365b08639f52387b33f3"
|
||||
|
||||
[[package]]
|
||||
name = "version_check"
|
||||
|
@ -1231,6 +1273,6 @@ checksum = "85e60b0d1b5f99db2556934e21937020776a5d31520bf169e851ac44e6420214"
|
|||
|
||||
[[package]]
|
||||
name = "zeroize"
|
||||
version = "1.2.0"
|
||||
version = "1.3.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "81a974bcdd357f0dca4d41677db03436324d45a4c9ed2d0b873a5a360ce41c36"
|
||||
checksum = "4756f7db3f7b5574938c3eb1c117038b8e07f95ee6718c0efad4ac21508f1efd"
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
[workspace]
|
||||
members = ["contracts/cw20-wrapped", "contracts/wormhole"]
|
||||
members = ["contracts/cw20-wrapped", "contracts/wormhole", "contracts/token-bridge"]
|
||||
|
||||
[profile.release]
|
||||
opt-level = 3
|
||||
|
|
|
@ -0,0 +1,6 @@
|
|||
#!/bin/bash
|
||||
|
||||
docker run --rm -v "$(pwd)":/code \
|
||||
--mount type=volume,source="$(basename "$(pwd)")_cache",target=/code/target \
|
||||
--mount type=volume,source=registry_cache,target=/usr/local/cargo/registry \
|
||||
cosmwasm/workspace-optimizer:0.10.7
|
|
@ -7,7 +7,7 @@ use cw20::Expiration;
|
|||
|
||||
#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, JsonSchema)]
|
||||
pub struct InitMsg {
|
||||
pub asset_chain: u8,
|
||||
pub asset_chain: u16,
|
||||
pub asset_address: Binary,
|
||||
pub decimals: u8,
|
||||
pub mint: Option<InitMint>,
|
||||
|
@ -105,7 +105,7 @@ pub enum QueryMsg {
|
|||
|
||||
#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, JsonSchema)]
|
||||
pub struct WrappedAssetInfoResponse {
|
||||
pub asset_chain: u8, // Asset chain id
|
||||
pub asset_chain: u16, // Asset chain id
|
||||
pub asset_address: Binary, // Asset smart contract address in the original chain
|
||||
pub bridge: HumanAddr, // Bridge address, authorized to mint and burn wrapped tokens
|
||||
}
|
||||
|
|
|
@ -9,7 +9,7 @@ pub const KEY_WRAPPED_ASSET: &[u8] = b"wrappedAsset";
|
|||
// Created at initialization and reference original asset and bridge address
|
||||
#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, JsonSchema)]
|
||||
pub struct WrappedAssetInfo {
|
||||
pub asset_chain: u8, // Asset chain id
|
||||
pub asset_chain: u16, // Asset chain id
|
||||
pub asset_address: Binary, // Asset smart contract address on the original chain
|
||||
pub bridge: CanonicalAddr, // Bridge address, authorized to mint and burn wrapped tokens
|
||||
}
|
||||
|
|
|
@ -0,0 +1,5 @@
|
|||
[alias]
|
||||
wasm = "build --release --target wasm32-unknown-unknown"
|
||||
wasm-debug = "build --target wasm32-unknown-unknown"
|
||||
unit-test = "test --lib --features backtraces"
|
||||
integration-test = "test --test integration"
|
|
@ -0,0 +1,36 @@
|
|||
[package]
|
||||
name = "token-bridge"
|
||||
version = "0.1.0"
|
||||
authors = ["Yuriy Savchenko <yuriy.savchenko@gmail.com>"]
|
||||
edition = "2018"
|
||||
description = "Wormhole token bridge"
|
||||
|
||||
[lib]
|
||||
crate-type = ["cdylib", "rlib"]
|
||||
|
||||
[features]
|
||||
backtraces = ["cosmwasm-std/backtraces"]
|
||||
# use library feature to disable all init/handle/query exports
|
||||
library = []
|
||||
|
||||
[dependencies]
|
||||
cosmwasm-std = { version = "0.10.0" }
|
||||
cosmwasm-storage = { version = "0.10.0" }
|
||||
schemars = "0.7"
|
||||
serde = { version = "1.0.103", default-features = false, features = ["derive"] }
|
||||
cw20 = "0.2.2"
|
||||
cw20-base = { version = "0.2.2", features = ["library"] }
|
||||
cw20-wrapped = { path = "../cw20-wrapped", features = ["library"] }
|
||||
wormhole = { path = "../wormhole", features = ["library"] }
|
||||
|
||||
thiserror = { version = "1.0.20" }
|
||||
k256 = { version = "0.5.9", default-features = false, features = ["ecdsa"] }
|
||||
sha3 = { version = "0.9.1", default-features = false }
|
||||
generic-array = { version = "0.14.4" }
|
||||
hex = "0.4.2"
|
||||
lazy_static = "1.4.0"
|
||||
bigint = "4"
|
||||
|
||||
[dev-dependencies]
|
||||
cosmwasm-vm = { version = "0.10.0", default-features = false, features = ["default-cranelift"] }
|
||||
serde_json = "1.0"
|
|
@ -0,0 +1,67 @@
|
|||
use cosmwasm_std::{CanonicalAddr, StdError, StdResult};
|
||||
|
||||
pub trait ByteUtils {
|
||||
fn get_u8(&self, index: usize) -> u8;
|
||||
fn get_u16(&self, index: usize) -> u16;
|
||||
fn get_u32(&self, index: usize) -> u32;
|
||||
fn get_u64(&self, index: usize) -> u64;
|
||||
|
||||
fn get_u128_be(&self, index: usize) -> u128;
|
||||
/// High 128 then low 128
|
||||
fn get_u256(&self, index: usize) -> (u128, u128);
|
||||
fn get_address(&self, index: usize) -> CanonicalAddr;
|
||||
fn get_bytes32(&self, index: usize) -> &[u8];
|
||||
}
|
||||
|
||||
impl ByteUtils for &[u8] {
|
||||
fn get_u8(&self, index: usize) -> u8 {
|
||||
self[index]
|
||||
}
|
||||
fn get_u16(&self, index: usize) -> u16 {
|
||||
let mut bytes: [u8; 16 / 8] = [0; 16 / 8];
|
||||
bytes.copy_from_slice(&self[index..index + 2]);
|
||||
u16::from_be_bytes(bytes)
|
||||
}
|
||||
fn get_u32(&self, index: usize) -> u32 {
|
||||
let mut bytes: [u8; 32 / 8] = [0; 32 / 8];
|
||||
bytes.copy_from_slice(&self[index..index + 4]);
|
||||
u32::from_be_bytes(bytes)
|
||||
}
|
||||
fn get_u64(&self, index: usize) -> u64 {
|
||||
let mut bytes: [u8; 64 / 8] = [0; 64 / 8];
|
||||
bytes.copy_from_slice(&self[index..index + 8]);
|
||||
u64::from_be_bytes(bytes)
|
||||
}
|
||||
fn get_u128_be(&self, index: usize) -> u128 {
|
||||
let mut bytes: [u8; 128 / 8] = [0; 128 / 8];
|
||||
bytes.copy_from_slice(&self[index..index + 128 / 8]);
|
||||
u128::from_be_bytes(bytes)
|
||||
}
|
||||
fn get_u256(&self, index: usize) -> (u128, u128) {
|
||||
(self.get_u128_be(index), self.get_u128_be(index + 128 / 8))
|
||||
}
|
||||
fn get_address(&self, index: usize) -> CanonicalAddr {
|
||||
// 32 bytes are reserved for addresses, but only the last 20 bytes are taken by the actual address
|
||||
CanonicalAddr::from(&self[index + 32 - 20..index + 32])
|
||||
}
|
||||
fn get_bytes32(&self, index: usize) -> &[u8] {
|
||||
&self[index..index + 32]
|
||||
}
|
||||
}
|
||||
|
||||
pub fn extend_address_to_32(addr: &CanonicalAddr) -> Vec<u8> {
|
||||
let mut result: Vec<u8> = vec![0; 12];
|
||||
result.extend(addr.as_slice());
|
||||
result
|
||||
}
|
||||
|
||||
pub fn extend_string_to_32(s: &String) -> StdResult<Vec<u8>> {
|
||||
let bytes = s.as_bytes();
|
||||
if bytes.len() > 32 {
|
||||
return Err(StdError::generic_err("string more than 32 "))
|
||||
}
|
||||
|
||||
let mut result = vec![0; 32 - bytes.len()];
|
||||
result.extend(bytes);
|
||||
Ok(result)
|
||||
}
|
|
@ -0,0 +1,548 @@
|
|||
use crate::msg::WrappedRegistryResponse;
|
||||
use cosmwasm_std::{
|
||||
log, to_binary, Api, Binary, CanonicalAddr, CosmosMsg, Env, Extern, HandleResponse, HumanAddr,
|
||||
InitResponse, Querier, QueryRequest, StdError, StdResult, Storage, Uint128, WasmMsg, WasmQuery,
|
||||
};
|
||||
|
||||
use crate::byte_utils::ByteUtils;
|
||||
use crate::byte_utils::{extend_address_to_32, extend_string_to_32};
|
||||
use crate::error::ContractError;
|
||||
use crate::msg::{HandleMsg, InitMsg, QueryMsg};
|
||||
use crate::state::{
|
||||
bridge_contracts, bridge_contracts_read, config, config_read, wrapped_asset,
|
||||
wrapped_asset_address, wrapped_asset_address_read, wrapped_asset_read, Action, AssetMeta,
|
||||
ConfigInfo, TokenBridgeMessage, TransferInfo,
|
||||
};
|
||||
|
||||
use cw20_base::msg::HandleMsg as TokenMsg;
|
||||
use cw20_base::msg::QueryMsg as TokenQuery;
|
||||
|
||||
use wormhole::msg::HandleMsg as WormholeHandleMsg;
|
||||
use wormhole::msg::QueryMsg as WormholeQueryMsg;
|
||||
|
||||
use wormhole::state::ParsedVAA;
|
||||
|
||||
use cw20::TokenInfoResponse;
|
||||
|
||||
use cw20_wrapped::msg::HandleMsg as WrappedMsg;
|
||||
use cw20_wrapped::msg::InitMsg as WrappedInit;
|
||||
use cw20_wrapped::msg::QueryMsg as WrappedQuery;
|
||||
use cw20_wrapped::msg::{InitHook, WrappedAssetInfoResponse};
|
||||
|
||||
use sha3::{Digest, Keccak256};
|
||||
|
||||
// Chain ID of Terra
|
||||
const CHAIN_ID: u16 = 3;
|
||||
|
||||
const WRAPPED_ASSET_UPDATING: &str = "updating";
|
||||
|
||||
pub fn init<S: Storage, A: Api, Q: Querier>(
|
||||
deps: &mut Extern<S, A, Q>,
|
||||
_env: Env,
|
||||
msg: InitMsg,
|
||||
) -> StdResult<InitResponse> {
|
||||
// Save general wormhole info
|
||||
let state = ConfigInfo {
|
||||
owner: msg.owner,
|
||||
wormhole_contract: msg.wormhole_contract,
|
||||
wrapped_asset_code_id: msg.wrapped_asset_code_id,
|
||||
};
|
||||
config(&mut deps.storage).save(&state)?;
|
||||
|
||||
Ok(InitResponse::default())
|
||||
}
|
||||
|
||||
pub fn parse_vaa<S: Storage, A: Api, Q: Querier>(
|
||||
deps: &mut Extern<S, A, Q>,
|
||||
block_time: u64,
|
||||
data: &Binary,
|
||||
) -> StdResult<ParsedVAA> {
|
||||
let cfg = config_read(&deps.storage).load()?;
|
||||
let vaa: ParsedVAA = deps.querier.query(&QueryRequest::Wasm(WasmQuery::Smart {
|
||||
contract_addr: cfg.wormhole_contract.clone(),
|
||||
msg: to_binary(&WormholeQueryMsg::VerifyVAA {
|
||||
vaa: data.clone(),
|
||||
block_time,
|
||||
})?,
|
||||
}))?;
|
||||
Ok(vaa)
|
||||
}
|
||||
|
||||
pub fn handle<S: Storage, A: Api, Q: Querier>(
|
||||
deps: &mut Extern<S, A, Q>,
|
||||
env: Env,
|
||||
msg: HandleMsg,
|
||||
) -> StdResult<HandleResponse> {
|
||||
match msg {
|
||||
HandleMsg::RegisterAssetHook { asset_id } => {
|
||||
handle_register_asset(deps, env, &asset_id.as_slice())
|
||||
}
|
||||
HandleMsg::InitiateTransfer {
|
||||
asset,
|
||||
amount,
|
||||
recipient_chain,
|
||||
recipient,
|
||||
nonce,
|
||||
} => handle_initiate_transfer(
|
||||
deps,
|
||||
env,
|
||||
asset,
|
||||
amount,
|
||||
recipient_chain,
|
||||
recipient.as_slice().to_vec(),
|
||||
nonce,
|
||||
),
|
||||
HandleMsg::SubmitVaa { data } => submit_vaa(deps, env, &data),
|
||||
HandleMsg::RegisterChain {
|
||||
chain_id,
|
||||
chain_address,
|
||||
} => handle_register_chain(deps, env, chain_id, chain_address.as_slice().to_vec()),
|
||||
HandleMsg::CreateAssetMeta {
|
||||
asset_address,
|
||||
nonce,
|
||||
} => handle_create_asset_meta(deps, env, &asset_address, nonce),
|
||||
}
|
||||
}
|
||||
|
||||
fn handle_register_chain<S: Storage, A: Api, Q: Querier>(
|
||||
deps: &mut Extern<S, A, Q>,
|
||||
env: Env,
|
||||
chain_id: u16,
|
||||
chain_address: Vec<u8>,
|
||||
) -> StdResult<HandleResponse> {
|
||||
let cfg = config_read(&deps.storage).load()?;
|
||||
|
||||
if env.message.sender != cfg.owner {
|
||||
return Err(StdError::unauthorized());
|
||||
}
|
||||
|
||||
let existing = bridge_contracts_read(&deps.storage).load(&chain_id.to_be_bytes());
|
||||
if existing.is_ok() {
|
||||
return Err(StdError::generic_err(
|
||||
"bridge contract already exists for this chain",
|
||||
));
|
||||
}
|
||||
|
||||
let mut bucket = bridge_contracts(&mut deps.storage);
|
||||
bucket.save(&chain_id.to_be_bytes(), &chain_address)?;
|
||||
|
||||
Ok(HandleResponse {
|
||||
messages: vec![],
|
||||
log: vec![
|
||||
log("chain_id", chain_id),
|
||||
log("chain_address", hex::encode(chain_address)),
|
||||
],
|
||||
data: None,
|
||||
})
|
||||
}
|
||||
|
||||
/// Handle wrapped asset registration messages
|
||||
fn handle_register_asset<S: Storage, A: Api, Q: Querier>(
|
||||
deps: &mut Extern<S, A, Q>,
|
||||
env: Env,
|
||||
asset_id: &[u8],
|
||||
) -> StdResult<HandleResponse> {
|
||||
let mut bucket = wrapped_asset(&mut deps.storage);
|
||||
let result = bucket.load(asset_id);
|
||||
let result = result.map_err(|_| ContractError::RegistrationForbidden.std())?;
|
||||
if result != HumanAddr::from(WRAPPED_ASSET_UPDATING) {
|
||||
return ContractError::AssetAlreadyRegistered.std_err();
|
||||
}
|
||||
|
||||
bucket.save(asset_id, &env.message.sender)?;
|
||||
|
||||
let contract_address: CanonicalAddr = deps.api.canonical_address(&env.message.sender)?;
|
||||
wrapped_asset_address(&mut deps.storage)
|
||||
.save(contract_address.as_slice(), &asset_id.to_vec())?;
|
||||
|
||||
Ok(HandleResponse {
|
||||
messages: vec![],
|
||||
log: vec![
|
||||
log("action", "register_asset"),
|
||||
log("asset_id", format!("{:?}", asset_id)),
|
||||
log("contract_addr", env.message.sender),
|
||||
],
|
||||
data: None,
|
||||
})
|
||||
}
|
||||
|
||||
fn handle_attest_meta<S: Storage, A: Api, Q: Querier>(
|
||||
deps: &mut Extern<S, A, Q>,
|
||||
env: Env,
|
||||
data: &Vec<u8>,
|
||||
) -> StdResult<HandleResponse> {
|
||||
let meta = AssetMeta::deserialize(data)?;
|
||||
if CHAIN_ID == meta.token_chain {
|
||||
return Err(StdError::generic_err("matching chain id, kinda cringe"));
|
||||
}
|
||||
|
||||
let cfg = config_read(&deps.storage).load()?;
|
||||
let asset_id = build_asset_id(meta.token_chain, &meta.token_address.as_slice());
|
||||
|
||||
wrapped_asset(&mut deps.storage).save(&asset_id, &HumanAddr::from(WRAPPED_ASSET_UPDATING))?;
|
||||
|
||||
Ok(HandleResponse {
|
||||
messages: vec![CosmosMsg::Wasm(WasmMsg::Instantiate {
|
||||
code_id: cfg.wrapped_asset_code_id,
|
||||
msg: to_binary(&WrappedInit {
|
||||
asset_chain: meta.token_chain,
|
||||
asset_address: meta.token_address.to_vec().into(),
|
||||
decimals: meta.decimals,
|
||||
mint: None,
|
||||
init_hook: Some(InitHook {
|
||||
contract_addr: env.contract.address,
|
||||
msg: to_binary(&HandleMsg::RegisterAssetHook {
|
||||
asset_id: asset_id.to_vec().into(),
|
||||
})?,
|
||||
}),
|
||||
})?,
|
||||
send: vec![],
|
||||
label: None,
|
||||
})],
|
||||
log: vec![],
|
||||
data: None,
|
||||
})
|
||||
}
|
||||
|
||||
fn handle_create_asset_meta<S: Storage, A: Api, Q: Querier>(
|
||||
deps: &mut Extern<S, A, Q>,
|
||||
env: Env,
|
||||
asset_address: &HumanAddr,
|
||||
nonce: u32,
|
||||
) -> StdResult<HandleResponse> {
|
||||
let cfg = config_read(&deps.storage).load()?;
|
||||
|
||||
let request = QueryRequest::<()>::Wasm(WasmQuery::Smart {
|
||||
contract_addr: asset_address.clone(),
|
||||
msg: to_binary(&TokenQuery::TokenInfo {})?,
|
||||
});
|
||||
|
||||
let asset_canonical = deps.api.canonical_address(asset_address)?;
|
||||
let token_info: TokenInfoResponse = deps.querier.custom_query(&request)?;
|
||||
|
||||
let meta: AssetMeta = AssetMeta {
|
||||
token_chain: CHAIN_ID,
|
||||
token_address: extend_address_to_32(&asset_canonical),
|
||||
decimals: token_info.decimals,
|
||||
symbol: extend_string_to_32(&token_info.symbol)?,
|
||||
name: extend_string_to_32(&token_info.name)?,
|
||||
};
|
||||
|
||||
let token_bridge_message = TokenBridgeMessage {
|
||||
action: Action::ATTEST_META,
|
||||
payload: meta.serialize().to_vec(),
|
||||
};
|
||||
|
||||
Ok(HandleResponse {
|
||||
messages: vec![CosmosMsg::Wasm(WasmMsg::Execute {
|
||||
contract_addr: cfg.wormhole_contract,
|
||||
msg: to_binary(&WormholeHandleMsg::PostMessage {
|
||||
message: Binary::from(token_bridge_message.serialize()),
|
||||
nonce,
|
||||
})?,
|
||||
// forward coins sent to this message
|
||||
send: env.message.sent_funds.clone(),
|
||||
})],
|
||||
log: vec![
|
||||
log("meta.token_chain", CHAIN_ID),
|
||||
log("meta.token", asset_address),
|
||||
log("meta.nonce", nonce),
|
||||
log("meta.block_time", env.block.time),
|
||||
],
|
||||
data: None,
|
||||
})
|
||||
}
|
||||
|
||||
fn submit_vaa<S: Storage, A: Api, Q: Querier>(
|
||||
deps: &mut Extern<S, A, Q>,
|
||||
env: Env,
|
||||
data: &Binary,
|
||||
) -> StdResult<HandleResponse> {
|
||||
let vaa = parse_vaa(deps, env.block.time, data)?;
|
||||
let data = vaa.payload;
|
||||
|
||||
let message = TokenBridgeMessage::deserialize(&data)?;
|
||||
|
||||
let result = match message.action {
|
||||
Action::TRANSFER => handle_complete_transfer(
|
||||
deps,
|
||||
env,
|
||||
vaa.emitter_chain,
|
||||
vaa.emitter_address,
|
||||
&message.payload,
|
||||
),
|
||||
Action::ATTEST_META => handle_attest_meta(deps, env, &message.payload),
|
||||
_ => ContractError::InvalidVAAAction.std_err(),
|
||||
};
|
||||
return result;
|
||||
}
|
||||
|
||||
fn handle_complete_transfer<S: Storage, A: Api, Q: Querier>(
|
||||
deps: &mut Extern<S, A, Q>,
|
||||
env: Env,
|
||||
emitter_chain: u16,
|
||||
emitter_address: Vec<u8>,
|
||||
data: &Vec<u8>,
|
||||
) -> StdResult<HandleResponse> {
|
||||
let transfer_info = TransferInfo::deserialize(&data)?;
|
||||
|
||||
let expected_contract =
|
||||
bridge_contracts_read(&deps.storage).load(&emitter_chain.to_be_bytes())?;
|
||||
|
||||
// must be sent by a registered token bridge contract
|
||||
if expected_contract != emitter_address {
|
||||
return Err(StdError::unauthorized());
|
||||
}
|
||||
|
||||
if transfer_info.recipient_chain != CHAIN_ID {
|
||||
return Err(StdError::generic_err(
|
||||
"you sent the message to the wrong chain, idiot",
|
||||
));
|
||||
}
|
||||
|
||||
let token_chain = transfer_info.token_chain;
|
||||
let target_address = (&transfer_info.recipient.as_slice()).get_address(0);
|
||||
|
||||
let (not_supported_amount, amount) = transfer_info.amount;
|
||||
|
||||
// Check high 128 bit of amount value to be empty
|
||||
if not_supported_amount != 0 {
|
||||
return ContractError::AmountTooHigh.std_err();
|
||||
}
|
||||
|
||||
if token_chain != CHAIN_ID {
|
||||
let asset_address = transfer_info.token_address;
|
||||
let asset_id = build_asset_id(token_chain, &asset_address);
|
||||
|
||||
// Check if this asset is already deployed
|
||||
let contract_addr = wrapped_asset_read(&deps.storage).load(&asset_id).ok();
|
||||
|
||||
return if let Some(contract_addr) = contract_addr {
|
||||
// Asset already deployed, just mint
|
||||
|
||||
let recipient = deps
|
||||
.api
|
||||
.human_address(&target_address)
|
||||
.or_else(|_| ContractError::WrongTargetAddressFormat.std_err())?;
|
||||
|
||||
Ok(HandleResponse {
|
||||
messages: vec![CosmosMsg::Wasm(WasmMsg::Execute {
|
||||
contract_addr: contract_addr.clone(),
|
||||
msg: to_binary(&WrappedMsg::Mint {
|
||||
recipient: recipient.clone(),
|
||||
amount: Uint128::from(amount),
|
||||
})?,
|
||||
send: vec![],
|
||||
})],
|
||||
log: vec![
|
||||
log("action", "complete_transfer_wrapped"),
|
||||
log("contract", contract_addr),
|
||||
log("recipient", recipient),
|
||||
log("amount", amount),
|
||||
],
|
||||
data: None,
|
||||
})
|
||||
} else {
|
||||
Err(StdError::generic_err("Wrapped asset not deployed. To deploy, invoke CreateWrapped with the associated AssetMeta"))
|
||||
};
|
||||
} else {
|
||||
let token_address = transfer_info.token_address.as_slice().get_address(0);
|
||||
|
||||
let recipient = deps.api.human_address(&target_address)?;
|
||||
let contract_addr = deps.api.human_address(&token_address)?;
|
||||
Ok(HandleResponse {
|
||||
messages: vec![CosmosMsg::Wasm(WasmMsg::Execute {
|
||||
contract_addr: contract_addr.clone(),
|
||||
msg: to_binary(&TokenMsg::Transfer {
|
||||
recipient: recipient.clone(),
|
||||
amount: Uint128::from(amount),
|
||||
})?,
|
||||
send: vec![],
|
||||
})],
|
||||
log: vec![
|
||||
log("action", "complete_transfer_native"),
|
||||
log("recipient", recipient),
|
||||
log("contract", contract_addr),
|
||||
log("amount", amount),
|
||||
],
|
||||
data: None,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
fn handle_initiate_transfer<S: Storage, A: Api, Q: Querier>(
|
||||
deps: &mut Extern<S, A, Q>,
|
||||
env: Env,
|
||||
asset: HumanAddr,
|
||||
amount: Uint128,
|
||||
recipient_chain: u16,
|
||||
recipient: Vec<u8>,
|
||||
nonce: u32,
|
||||
) -> StdResult<HandleResponse> {
|
||||
// if recipient_chain == CHAIN_ID {
|
||||
// return ContractError::SameSourceAndTarget.std_err();
|
||||
// }
|
||||
|
||||
if amount.is_zero() {
|
||||
return ContractError::AmountTooLow.std_err();
|
||||
}
|
||||
|
||||
let asset_chain: u16;
|
||||
let asset_address: Vec<u8>;
|
||||
|
||||
let cfg: ConfigInfo = config_read(&deps.storage).load()?;
|
||||
let asset_canonical: CanonicalAddr = deps.api.canonical_address(&asset)?;
|
||||
|
||||
let mut messages: Vec<CosmosMsg> = vec![];
|
||||
|
||||
match wrapped_asset_address_read(&deps.storage).load(asset_canonical.as_slice()) {
|
||||
Ok(_) => {
|
||||
// This is a deployed wrapped asset, burn it
|
||||
messages.push(CosmosMsg::Wasm(WasmMsg::Execute {
|
||||
contract_addr: asset.clone(),
|
||||
msg: to_binary(&WrappedMsg::Burn {
|
||||
account: env.message.sender.clone(),
|
||||
amount,
|
||||
})?,
|
||||
send: vec![],
|
||||
}));
|
||||
let request = QueryRequest::<()>::Wasm(WasmQuery::Smart {
|
||||
contract_addr: asset,
|
||||
msg: to_binary(&WrappedQuery::WrappedAssetInfo {})?,
|
||||
});
|
||||
let wrapped_token_info: WrappedAssetInfoResponse =
|
||||
deps.querier.custom_query(&request)?;
|
||||
asset_chain = wrapped_token_info.asset_chain;
|
||||
asset_address = wrapped_token_info.asset_address.as_slice().to_vec();
|
||||
}
|
||||
Err(_) => {
|
||||
// This is a regular asset, transfer its balance
|
||||
messages.push(CosmosMsg::Wasm(WasmMsg::Execute {
|
||||
contract_addr: asset,
|
||||
msg: to_binary(&TokenMsg::TransferFrom {
|
||||
owner: env.message.sender.clone(),
|
||||
recipient: env.contract.address.clone(),
|
||||
amount,
|
||||
})?,
|
||||
send: vec![],
|
||||
}));
|
||||
asset_address = extend_address_to_32(&asset_canonical);
|
||||
asset_chain = CHAIN_ID;
|
||||
}
|
||||
};
|
||||
|
||||
let transfer_info = TransferInfo {
|
||||
token_chain: asset_chain,
|
||||
token_address: asset_address.clone(),
|
||||
amount: (0, amount.u128()),
|
||||
recipient_chain,
|
||||
recipient: recipient.clone(),
|
||||
};
|
||||
|
||||
let token_bridge_message = TokenBridgeMessage {
|
||||
action: Action::TRANSFER,
|
||||
payload: transfer_info.serialize(),
|
||||
};
|
||||
|
||||
messages.push(CosmosMsg::Wasm(WasmMsg::Execute {
|
||||
contract_addr: cfg.wormhole_contract,
|
||||
msg: to_binary(&WormholeHandleMsg::PostMessage {
|
||||
message: Binary::from(token_bridge_message.serialize()),
|
||||
nonce,
|
||||
})?,
|
||||
// forward coins sent to this message
|
||||
send: env.message.sent_funds.clone(),
|
||||
}));
|
||||
|
||||
Ok(HandleResponse {
|
||||
messages,
|
||||
log: vec![
|
||||
log("transfer.token_chain", asset_chain),
|
||||
log("transfer.token", hex::encode(asset_address)),
|
||||
log(
|
||||
"transfer.sender",
|
||||
hex::encode(extend_address_to_32(
|
||||
&deps.api.canonical_address(&env.message.sender)?,
|
||||
)),
|
||||
),
|
||||
log("transfer.recipient_chain", recipient_chain),
|
||||
log("transfer.recipient", hex::encode(recipient)),
|
||||
log("transfer.amount", amount),
|
||||
log("transfer.nonce", nonce),
|
||||
log("transfer.block_time", env.block.time),
|
||||
],
|
||||
data: None,
|
||||
})
|
||||
}
|
||||
|
||||
pub fn query<S: Storage, A: Api, Q: Querier>(
|
||||
deps: &Extern<S, A, Q>,
|
||||
msg: QueryMsg,
|
||||
) -> StdResult<Binary> {
|
||||
match msg {
|
||||
QueryMsg::WrappedRegistry { chain, address } => {
|
||||
to_binary(&query_wrapped_registry(deps, chain, address.as_slice())?)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn query_wrapped_registry<S: Storage, A: Api, Q: Querier>(
|
||||
deps: &Extern<S, A, Q>,
|
||||
chain: u16,
|
||||
address: &[u8],
|
||||
) -> StdResult<WrappedRegistryResponse> {
|
||||
let asset_id = build_asset_id(chain, address);
|
||||
// Check if this asset is already deployed
|
||||
match wrapped_asset_read(&deps.storage).load(&asset_id) {
|
||||
Ok(address) => Ok(WrappedRegistryResponse { address }),
|
||||
Err(_) => ContractError::AssetNotFound.std_err(),
|
||||
}
|
||||
}
|
||||
|
||||
fn build_asset_id(chain: u16, address: &[u8]) -> Vec<u8> {
|
||||
let mut asset_id: Vec<u8> = vec![];
|
||||
asset_id.extend_from_slice(&chain.to_be_bytes());
|
||||
asset_id.extend_from_slice(address);
|
||||
|
||||
let mut hasher = Keccak256::new();
|
||||
hasher.update(asset_id);
|
||||
hasher.finalize().to_vec()
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use cosmwasm_std::{to_binary, Binary, StdResult};
|
||||
|
||||
#[test]
|
||||
fn test_me() -> StdResult<()> {
|
||||
let x = vec![
|
||||
1u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 96u8, 180u8, 94u8, 195u8, 0u8, 0u8,
|
||||
0u8, 1u8, 0u8, 3u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 38u8,
|
||||
229u8, 4u8, 215u8, 149u8, 163u8, 42u8, 54u8, 156u8, 236u8, 173u8, 168u8, 72u8, 220u8,
|
||||
100u8, 90u8, 154u8, 159u8, 160u8, 215u8, 0u8, 91u8, 48u8, 44u8, 48u8, 44u8, 51u8, 44u8,
|
||||
48u8, 44u8, 48u8, 44u8, 48u8, 44u8, 48u8, 44u8, 48u8, 44u8, 48u8, 44u8, 48u8, 44u8,
|
||||
48u8, 44u8, 48u8, 44u8, 48u8, 44u8, 48u8, 44u8, 48u8, 44u8, 53u8, 55u8, 44u8, 52u8,
|
||||
54u8, 44u8, 50u8, 53u8, 53u8, 44u8, 53u8, 48u8, 44u8, 50u8, 52u8, 51u8, 44u8, 49u8,
|
||||
48u8, 54u8, 44u8, 49u8, 50u8, 50u8, 44u8, 49u8, 49u8, 48u8, 44u8, 49u8, 50u8, 53u8,
|
||||
44u8, 56u8, 56u8, 44u8, 55u8, 51u8, 44u8, 49u8, 56u8, 57u8, 44u8, 50u8, 48u8, 55u8,
|
||||
44u8, 49u8, 48u8, 52u8, 44u8, 56u8, 51u8, 44u8, 49u8, 49u8, 57u8, 44u8, 49u8, 50u8,
|
||||
55u8, 44u8, 49u8, 57u8, 50u8, 44u8, 49u8, 52u8, 55u8, 44u8, 56u8, 57u8, 44u8, 48u8,
|
||||
44u8, 48u8, 44u8, 48u8, 44u8, 48u8, 44u8, 48u8, 44u8, 48u8, 44u8, 48u8, 44u8, 48u8,
|
||||
44u8, 48u8, 44u8, 48u8, 44u8, 48u8, 44u8, 48u8, 44u8, 48u8, 44u8, 48u8, 44u8, 48u8,
|
||||
44u8, 48u8, 44u8, 48u8, 44u8, 48u8, 44u8, 48u8, 44u8, 48u8, 44u8, 48u8, 44u8, 48u8,
|
||||
44u8, 48u8, 44u8, 48u8, 44u8, 48u8, 44u8, 48u8, 44u8, 48u8, 44u8, 48u8, 44u8, 48u8,
|
||||
44u8, 48u8, 44u8, 51u8, 44u8, 50u8, 51u8, 50u8, 44u8, 48u8, 44u8, 51u8, 44u8, 48u8,
|
||||
44u8, 48u8, 44u8, 48u8, 44u8, 48u8, 44u8, 48u8, 44u8, 48u8, 44u8, 48u8, 44u8, 48u8,
|
||||
44u8, 48u8, 44u8, 48u8, 44u8, 48u8, 44u8, 48u8, 44u8, 53u8, 51u8, 44u8, 49u8, 49u8,
|
||||
54u8, 44u8, 52u8, 56u8, 44u8, 49u8, 49u8, 54u8, 44u8, 49u8, 52u8, 57u8, 44u8, 49u8,
|
||||
48u8, 56u8, 44u8, 49u8, 49u8, 51u8, 44u8, 56u8, 44u8, 48u8, 44u8, 50u8, 51u8, 50u8,
|
||||
44u8, 52u8, 57u8, 44u8, 49u8, 53u8, 50u8, 44u8, 49u8, 44u8, 50u8, 56u8, 44u8, 50u8,
|
||||
48u8, 51u8, 44u8, 50u8, 49u8, 50u8, 44u8, 50u8, 50u8, 49u8, 44u8, 50u8, 52u8, 49u8,
|
||||
44u8, 56u8, 53u8, 44u8, 49u8, 48u8, 57u8, 93u8,
|
||||
];
|
||||
let b = Binary::from(x.clone());
|
||||
let y = b.as_slice().to_vec();
|
||||
assert_eq!(x, y);
|
||||
Ok(())
|
||||
}
|
||||
}
|
|
@ -0,0 +1,114 @@
|
|||
use cosmwasm_std::StdError;
|
||||
use thiserror::Error;
|
||||
|
||||
#[derive(Error, Debug)]
|
||||
pub enum ContractError {
|
||||
/// Invalid VAA version
|
||||
#[error("InvalidVersion")]
|
||||
InvalidVersion,
|
||||
|
||||
/// Guardian set with this index does not exist
|
||||
#[error("InvalidGuardianSetIndex")]
|
||||
InvalidGuardianSetIndex,
|
||||
|
||||
/// Guardian set expiration date is zero or in the past
|
||||
#[error("GuardianSetExpired")]
|
||||
GuardianSetExpired,
|
||||
|
||||
/// Not enough signers on the VAA
|
||||
#[error("NoQuorum")]
|
||||
NoQuorum,
|
||||
|
||||
/// Wrong guardian index order, order must be ascending
|
||||
#[error("WrongGuardianIndexOrder")]
|
||||
WrongGuardianIndexOrder,
|
||||
|
||||
/// Some problem with signature decoding from bytes
|
||||
#[error("CannotDecodeSignature")]
|
||||
CannotDecodeSignature,
|
||||
|
||||
/// Some problem with public key recovery from the signature
|
||||
#[error("CannotRecoverKey")]
|
||||
CannotRecoverKey,
|
||||
|
||||
/// Recovered pubkey from signature does not match guardian address
|
||||
#[error("GuardianSignatureError")]
|
||||
GuardianSignatureError,
|
||||
|
||||
/// VAA action code not recognized
|
||||
#[error("InvalidVAAAction")]
|
||||
InvalidVAAAction,
|
||||
|
||||
/// VAA guardian set is not current
|
||||
#[error("NotCurrentGuardianSet")]
|
||||
NotCurrentGuardianSet,
|
||||
|
||||
/// Only 128-bit amounts are supported
|
||||
#[error("AmountTooHigh")]
|
||||
AmountTooHigh,
|
||||
|
||||
/// Amount should be higher than zero
|
||||
#[error("AmountTooLow")]
|
||||
AmountTooLow,
|
||||
|
||||
/// Source and target chain ids must be different
|
||||
#[error("SameSourceAndTarget")]
|
||||
SameSourceAndTarget,
|
||||
|
||||
/// Target chain id must be the same as the current CHAIN_ID
|
||||
#[error("WrongTargetChain")]
|
||||
WrongTargetChain,
|
||||
|
||||
/// Wrapped asset init hook sent twice for the same asset id
|
||||
#[error("AssetAlreadyRegistered")]
|
||||
AssetAlreadyRegistered,
|
||||
|
||||
/// Guardian set must increase in steps of 1
|
||||
#[error("GuardianSetIndexIncreaseError")]
|
||||
GuardianSetIndexIncreaseError,
|
||||
|
||||
/// VAA was already executed
|
||||
#[error("VaaAlreadyExecuted")]
|
||||
VaaAlreadyExecuted,
|
||||
|
||||
/// Message sender not permitted to execute this operation
|
||||
#[error("PermissionDenied")]
|
||||
PermissionDenied,
|
||||
|
||||
/// Could not decode target address from canonical to human-readable form
|
||||
#[error("WrongTargetAddressFormat")]
|
||||
WrongTargetAddressFormat,
|
||||
|
||||
/// More signatures than active guardians found
|
||||
#[error("TooManySignatures")]
|
||||
TooManySignatures,
|
||||
|
||||
/// Wrapped asset not found in the registry
|
||||
#[error("AssetNotFound")]
|
||||
AssetNotFound,
|
||||
|
||||
/// Generic error when there is a problem with VAA structure
|
||||
#[error("InvalidVAA")]
|
||||
InvalidVAA,
|
||||
|
||||
/// Thrown when fee is enabled for the action, but was not sent with the transaction
|
||||
#[error("FeeTooLow")]
|
||||
FeeTooLow,
|
||||
|
||||
/// Registering asset outside of the wormhole
|
||||
#[error("RegistrationForbidden")]
|
||||
RegistrationForbidden,
|
||||
}
|
||||
|
||||
impl ContractError {
|
||||
pub fn std(&self) -> StdError {
|
||||
StdError::GenericErr {
|
||||
msg: format!("{}", self),
|
||||
backtrace: None,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn std_err<T>(&self) -> Result<T, StdError> {
|
||||
Err(self.std())
|
||||
}
|
||||
}
|
|
@ -0,0 +1,14 @@
|
|||
#[cfg(test)]
|
||||
#[macro_use]
|
||||
extern crate lazy_static;
|
||||
|
||||
mod byte_utils;
|
||||
pub mod contract;
|
||||
mod error;
|
||||
pub mod msg;
|
||||
pub mod state;
|
||||
|
||||
pub use crate::error::ContractError;
|
||||
|
||||
#[cfg(all(target_arch = "wasm32", not(feature = "library")))]
|
||||
cosmwasm_std::create_entry_points!(contract);
|
|
@ -0,0 +1,60 @@
|
|||
use cosmwasm_std::{Binary, HumanAddr, Uint128};
|
||||
use schemars::JsonSchema;
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, JsonSchema)]
|
||||
pub struct InitMsg {
|
||||
pub owner: HumanAddr,
|
||||
pub wormhole_contract: HumanAddr,
|
||||
pub wrapped_asset_code_id: u64,
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, JsonSchema)]
|
||||
#[serde(rename_all = "snake_case")]
|
||||
pub enum HandleMsg {
|
||||
|
||||
|
||||
RegisterAssetHook {
|
||||
asset_id: Binary,
|
||||
},
|
||||
|
||||
InitiateTransfer {
|
||||
asset: HumanAddr,
|
||||
amount: Uint128,
|
||||
recipient_chain: u16,
|
||||
recipient: Binary,
|
||||
nonce: u32,
|
||||
},
|
||||
|
||||
SubmitVaa {
|
||||
data: Binary,
|
||||
},
|
||||
|
||||
RegisterChain {
|
||||
chain_id: u16,
|
||||
chain_address: Binary,
|
||||
},
|
||||
|
||||
CreateAssetMeta {
|
||||
asset_address: HumanAddr,
|
||||
nonce: u32,
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, JsonSchema)]
|
||||
#[serde(rename_all = "snake_case")]
|
||||
pub enum QueryMsg {
|
||||
WrappedRegistry { chain: u16, address: Binary },
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, JsonSchema)]
|
||||
#[serde(rename_all = "snake_case")]
|
||||
pub struct WrappedRegistryResponse {
|
||||
pub address: HumanAddr,
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, JsonSchema)]
|
||||
#[serde(rename_all = "snake_case")]
|
||||
pub enum WormholeQueryMsg {
|
||||
VerifyVAA { vaa: Binary, block_time: u64 },
|
||||
}
|
|
@ -0,0 +1,186 @@
|
|||
use schemars::JsonSchema;
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
use cosmwasm_std::{HumanAddr, StdResult, Storage};
|
||||
use cosmwasm_storage::{
|
||||
bucket, bucket_read, singleton, singleton_read, Bucket, ReadonlyBucket, ReadonlySingleton,
|
||||
Singleton,
|
||||
};
|
||||
|
||||
use crate::byte_utils::ByteUtils;
|
||||
|
||||
|
||||
pub static CONFIG_KEY: &[u8] = b"config";
|
||||
pub static WRAPPED_ASSET_KEY: &[u8] = b"wrapped_asset";
|
||||
pub static WRAPPED_ASSET_ADDRESS_KEY: &[u8] = b"wrapped_asset_address";
|
||||
pub static BRIDGE_CONTRACTS: &[u8] = b"bridge_contracts";
|
||||
|
||||
// Guardian set information
|
||||
#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, JsonSchema)]
|
||||
pub struct ConfigInfo {
|
||||
// Current active guardian set
|
||||
pub owner: HumanAddr,
|
||||
pub wormhole_contract: HumanAddr,
|
||||
pub wrapped_asset_code_id: u64,
|
||||
}
|
||||
|
||||
pub fn config<S: Storage>(storage: &mut S) -> Singleton<S, ConfigInfo> {
|
||||
singleton(storage, CONFIG_KEY)
|
||||
}
|
||||
|
||||
pub fn config_read<S: Storage>(storage: &S) -> ReadonlySingleton<S, ConfigInfo> {
|
||||
singleton_read(storage, CONFIG_KEY)
|
||||
}
|
||||
|
||||
pub fn bridge_contracts<S: Storage>(storage: &mut S) -> Bucket<S, Vec<u8>> {
|
||||
bucket(BRIDGE_CONTRACTS, storage)
|
||||
}
|
||||
|
||||
pub fn bridge_contracts_read<S: Storage>(storage: &S) -> ReadonlyBucket<S, Vec<u8>> {
|
||||
bucket_read(BRIDGE_CONTRACTS, storage)
|
||||
}
|
||||
|
||||
pub fn wrapped_asset<S: Storage>(storage: &mut S) -> Bucket<S, HumanAddr> {
|
||||
bucket(WRAPPED_ASSET_KEY, storage)
|
||||
}
|
||||
|
||||
pub fn wrapped_asset_read<S: Storage>(storage: &S) -> ReadonlyBucket<S, HumanAddr> {
|
||||
bucket_read(WRAPPED_ASSET_KEY, storage)
|
||||
}
|
||||
|
||||
pub fn wrapped_asset_address<S: Storage>(storage: &mut S) -> Bucket<S, Vec<u8>> {
|
||||
bucket(WRAPPED_ASSET_ADDRESS_KEY, storage)
|
||||
}
|
||||
|
||||
pub fn wrapped_asset_address_read<S: Storage>(storage: &S) -> ReadonlyBucket<S, Vec<u8>> {
|
||||
bucket_read(WRAPPED_ASSET_ADDRESS_KEY, storage)
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
pub struct Action;
|
||||
|
||||
impl Action {
|
||||
pub const TRANSFER: u8 = 0;
|
||||
pub const ATTEST_META: u8 = 1;
|
||||
}
|
||||
|
||||
// 0 u8 action
|
||||
// 1 [u8] payload
|
||||
|
||||
pub struct TokenBridgeMessage {
|
||||
pub action: u8,
|
||||
pub payload: Vec<u8>,
|
||||
}
|
||||
|
||||
impl TokenBridgeMessage {
|
||||
pub fn deserialize(data: &Vec<u8>) -> StdResult<Self> {
|
||||
let data = data.as_slice();
|
||||
let action = data.get_u8(0);
|
||||
let payload = &data[1..];
|
||||
|
||||
Ok(TokenBridgeMessage {
|
||||
action,
|
||||
payload: payload.to_vec(),
|
||||
})
|
||||
}
|
||||
|
||||
pub fn serialize(&self) ->Vec<u8> {
|
||||
[self.action.to_be_bytes().to_vec(), self.payload.clone()].concat()
|
||||
}
|
||||
}
|
||||
|
||||
// 0 u16 token_chain
|
||||
// 2 [u8; 32] token_address
|
||||
// 34 u256 amount
|
||||
// 66 u16 recipient_chain
|
||||
// 68 [u8; 32] recipient
|
||||
|
||||
pub struct TransferInfo {
|
||||
pub token_chain: u16,
|
||||
pub token_address: Vec<u8>,
|
||||
pub amount: (u128, u128),
|
||||
pub recipient_chain: u16,
|
||||
pub recipient: Vec<u8>,
|
||||
}
|
||||
|
||||
impl TransferInfo {
|
||||
pub fn deserialize(data: &Vec<u8>) -> StdResult<Self> {
|
||||
let data = data.as_slice();
|
||||
let token_chain = data.get_u16(0);
|
||||
let token_address = data.get_bytes32(2).to_vec();
|
||||
let amount = data.get_u256(34);
|
||||
let recipient_chain = data.get_u16(66);
|
||||
let recipient = data.get_bytes32(68).to_vec();
|
||||
|
||||
Ok(TransferInfo {
|
||||
token_chain,
|
||||
token_address,
|
||||
amount,
|
||||
recipient_chain,
|
||||
recipient,
|
||||
})
|
||||
}
|
||||
pub fn serialize(&self) -> Vec<u8> {
|
||||
[
|
||||
self.token_chain.to_be_bytes().to_vec(),
|
||||
self.token_address.clone(),
|
||||
self.amount.0.to_be_bytes().to_vec(),
|
||||
self.amount.1.to_be_bytes().to_vec(),
|
||||
self.recipient_chain.to_be_bytes().to_vec(),
|
||||
self.recipient.to_vec(),
|
||||
]
|
||||
.concat()
|
||||
}
|
||||
}
|
||||
|
||||
//PayloadID uint8 = 2
|
||||
// // Address of the token. Left-zero-padded if shorter than 32 bytes
|
||||
// TokenAddress [32]uint8
|
||||
// // Chain ID of the token
|
||||
// TokenChain uint16
|
||||
// // Number of decimals of the token (big-endian uint256)
|
||||
// Decimals [32]uint8
|
||||
// // Symbol of the token (UTF-8)
|
||||
// Symbol [32]uint8
|
||||
// // Name of the token (UTF-8)
|
||||
// Name [32]uint8
|
||||
|
||||
pub struct AssetMeta {
|
||||
pub token_chain: u16,
|
||||
pub token_address: Vec<u8>,
|
||||
pub decimals: u8,
|
||||
pub symbol: Vec<u8>,
|
||||
pub name: Vec<u8>,
|
||||
}
|
||||
|
||||
impl AssetMeta {
|
||||
pub fn deserialize(data: &Vec<u8>) -> StdResult<Self> {
|
||||
let data = data.as_slice();
|
||||
let token_chain = data.get_u16(0);
|
||||
let token_address = data.get_bytes32(2).to_vec();
|
||||
let decimals = data.get_u8(34);
|
||||
let symbol = data.get_bytes32(35).to_vec();
|
||||
let name = data.get_bytes32(67).to_vec();
|
||||
|
||||
Ok(AssetMeta {
|
||||
token_chain,
|
||||
token_address,
|
||||
decimals,
|
||||
symbol,
|
||||
name,
|
||||
})
|
||||
}
|
||||
|
||||
pub fn serialize(&self) -> Vec<u8> {
|
||||
[
|
||||
self.token_chain.to_be_bytes().to_vec(),
|
||||
self.token_address.clone(),
|
||||
self.decimals.to_be_bytes().to_vec(),
|
||||
self.symbol.clone(),
|
||||
self.name.clone(),
|
||||
]
|
||||
.concat()
|
||||
}
|
||||
}
|
|
@ -0,0 +1,90 @@
|
|||
static WASM: &[u8] = include_bytes!("../../../target/wasm32-unknown-unknown/release/wormhole.wasm");
|
||||
|
||||
use cosmwasm_std::{from_slice, Env, HumanAddr, InitResponse, Coin};
|
||||
use cosmwasm_storage::to_length_prefixed;
|
||||
use cosmwasm_vm::testing::{init, mock_env, mock_instance, MockApi, MockQuerier, MockStorage};
|
||||
use cosmwasm_vm::{Api, Instance, Storage};
|
||||
|
||||
use wormhole::msg::InitMsg;
|
||||
use wormhole::state::{ConfigInfo, GuardianAddress, GuardianSetInfo, CONFIG_KEY};
|
||||
|
||||
use hex;
|
||||
|
||||
enum TestAddress {
|
||||
INITIALIZER,
|
||||
}
|
||||
|
||||
impl TestAddress {
|
||||
fn value(&self) -> HumanAddr {
|
||||
match self {
|
||||
TestAddress::INITIALIZER => HumanAddr::from("initializer"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn mock_env_height(signer: &HumanAddr, height: u64, time: u64) -> Env {
|
||||
let mut env = mock_env(signer, &[]);
|
||||
env.block.height = height;
|
||||
env.block.time = time;
|
||||
env
|
||||
}
|
||||
|
||||
fn get_config_info<S: Storage>(storage: &S) -> ConfigInfo {
|
||||
let key = to_length_prefixed(CONFIG_KEY);
|
||||
let data = storage
|
||||
.get(&key)
|
||||
.0
|
||||
.expect("error getting data")
|
||||
.expect("data should exist");
|
||||
from_slice(&data).expect("invalid data")
|
||||
}
|
||||
|
||||
fn do_init(
|
||||
height: u64,
|
||||
guardians: &Vec<GuardianAddress>,
|
||||
) -> Instance<MockStorage, MockApi, MockQuerier> {
|
||||
let mut deps = mock_instance(WASM, &[]);
|
||||
let init_msg = InitMsg {
|
||||
initial_guardian_set: GuardianSetInfo {
|
||||
addresses: guardians.clone(),
|
||||
expiration_time: 100,
|
||||
},
|
||||
guardian_set_expirity: 50,
|
||||
wrapped_asset_code_id: 999,
|
||||
};
|
||||
let env = mock_env_height(&TestAddress::INITIALIZER.value(), height, 0);
|
||||
let owner = deps
|
||||
.api
|
||||
.canonical_address(&TestAddress::INITIALIZER.value())
|
||||
.0
|
||||
.unwrap();
|
||||
let res: InitResponse = init(&mut deps, env, init_msg).unwrap();
|
||||
assert_eq!(0, res.messages.len());
|
||||
|
||||
// query the store directly
|
||||
deps.with_storage(|storage| {
|
||||
assert_eq!(
|
||||
get_config_info(storage),
|
||||
ConfigInfo {
|
||||
guardian_set_index: 0,
|
||||
guardian_set_expirity: 50,
|
||||
wrapped_asset_code_id: 999,
|
||||
owner,
|
||||
fee: Coin::new(10000, "uluna"),
|
||||
}
|
||||
);
|
||||
Ok(())
|
||||
})
|
||||
.unwrap();
|
||||
deps
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn init_works() {
|
||||
let guardians = vec![GuardianAddress::from(GuardianAddress {
|
||||
bytes: hex::decode("beFA429d57cD18b7F8A4d91A2da9AB4AF05d0FBe")
|
||||
.expect("Decoding failed")
|
||||
.into(),
|
||||
})];
|
||||
let _deps = do_init(111, &guardians);
|
||||
}
|
|
@ -1,8 +1,11 @@
|
|||
use cosmwasm_std::CanonicalAddr;
|
||||
use cosmwasm_std::{CanonicalAddr, StdError, StdResult};
|
||||
|
||||
pub trait ByteUtils {
|
||||
fn get_u8(&self, index: usize) -> u8;
|
||||
fn get_u16(&self, index: usize) -> u16;
|
||||
fn get_u32(&self, index: usize) -> u32;
|
||||
fn get_u64(&self, index: usize) -> u64;
|
||||
|
||||
fn get_u128_be(&self, index: usize) -> u128;
|
||||
/// High 128 then low 128
|
||||
fn get_u256(&self, index: usize) -> (u128, u128);
|
||||
|
@ -14,11 +17,21 @@ impl ByteUtils for &[u8] {
|
|||
fn get_u8(&self, index: usize) -> u8 {
|
||||
self[index]
|
||||
}
|
||||
fn get_u16(&self, index: usize) -> u16 {
|
||||
let mut bytes: [u8; 16 / 8] = [0; 16 / 8];
|
||||
bytes.copy_from_slice(&self[index..index + 2]);
|
||||
u16::from_be_bytes(bytes)
|
||||
}
|
||||
fn get_u32(&self, index: usize) -> u32 {
|
||||
let mut bytes: [u8; 32 / 8] = [0; 32 / 8];
|
||||
bytes.copy_from_slice(&self[index..index + 4]);
|
||||
u32::from_be_bytes(bytes)
|
||||
}
|
||||
fn get_u64(&self, index: usize) -> u64 {
|
||||
let mut bytes: [u8; 64 / 8] = [0; 64 / 8];
|
||||
bytes.copy_from_slice(&self[index..index + 8]);
|
||||
u64::from_be_bytes(bytes)
|
||||
}
|
||||
fn get_u128_be(&self, index: usize) -> u128 {
|
||||
let mut bytes: [u8; 128 / 8] = [0; 128 / 8];
|
||||
bytes.copy_from_slice(&self[index..index + 128 / 8]);
|
||||
|
@ -41,3 +54,14 @@ pub fn extend_address_to_32(addr: &CanonicalAddr) -> Vec<u8> {
|
|||
result.extend(addr.as_slice());
|
||||
result
|
||||
}
|
||||
|
||||
pub fn extend_string_to_32(s: &String) -> StdResult<Vec<u8>> {
|
||||
let bytes = s.as_bytes();
|
||||
if bytes.len() > 32 {
|
||||
return Err(StdError::generic_err("string more than 32 "))
|
||||
}
|
||||
|
||||
let mut result = vec![0; 32 - bytes.len()];
|
||||
result.extend(bytes);
|
||||
Ok(result)
|
||||
}
|
|
@ -1,29 +1,19 @@
|
|||
use crate::msg::WrappedRegistryResponse;
|
||||
use cosmwasm_std::{
|
||||
log, to_binary, Api, Binary, CanonicalAddr, CosmosMsg, BankMsg, Env, Extern, HandleResponse, HumanAddr,
|
||||
InitResponse, Querier, QueryRequest, StdResult, Storage, Uint128, WasmMsg, WasmQuery, Coin, has_coins,
|
||||
has_coins, log, to_binary, Api, BankMsg, Binary, Coin, CosmosMsg, Env, Extern, HandleResponse,
|
||||
HumanAddr, InitResponse, Querier, StdError, StdResult, Storage,
|
||||
};
|
||||
|
||||
use crate::byte_utils::extend_address_to_32;
|
||||
use crate::byte_utils::ByteUtils;
|
||||
use crate::error::ContractError;
|
||||
use crate::msg::{GuardianSetInfoResponse, HandleMsg, InitMsg, QueryMsg, GetStateResponse};
|
||||
use crate::msg::{
|
||||
GetAddressHexResponse, GetStateResponse, GuardianSetInfoResponse, HandleMsg, InitMsg, QueryMsg,
|
||||
};
|
||||
use crate::state::{
|
||||
config, config_read, guardian_set_get, guardian_set_set, vaa_archive_add, vaa_archive_check,
|
||||
wrapped_asset, wrapped_asset_address, wrapped_asset_address_read, wrapped_asset_read,
|
||||
ConfigInfo, GuardianAddress, GuardianSetInfo, ParsedVAA,
|
||||
ConfigInfo, GuardianAddress, GuardianSetInfo, ParsedVAA, WormholeGovernance,
|
||||
};
|
||||
|
||||
use cw20_base::msg::HandleMsg as TokenMsg;
|
||||
use cw20_base::msg::QueryMsg as TokenQuery;
|
||||
|
||||
use cw20::TokenInfoResponse;
|
||||
|
||||
use cw20_wrapped::msg::HandleMsg as WrappedMsg;
|
||||
use cw20_wrapped::msg::InitMsg as WrappedInit;
|
||||
use cw20_wrapped::msg::QueryMsg as WrappedQuery;
|
||||
use cw20_wrapped::msg::{InitHook, InitMint, WrappedAssetInfoResponse};
|
||||
|
||||
use k256::ecdsa::recoverable::Id as RecoverableId;
|
||||
use k256::ecdsa::recoverable::Signature as RecoverableSignature;
|
||||
use k256::ecdsa::Signature;
|
||||
|
@ -36,14 +26,12 @@ use generic_array::GenericArray;
|
|||
use std::convert::TryFrom;
|
||||
|
||||
// Chain ID of Terra
|
||||
const CHAIN_ID: u8 = 3;
|
||||
const CHAIN_ID: u16 = 3;
|
||||
|
||||
// Lock assets fee amount and denomination
|
||||
const FEE_AMOUNT: u128 = 10000;
|
||||
const FEE_DENOMINATION: &str = "uluna";
|
||||
|
||||
const WRAPPED_ASSET_UPDATING: &str = "updating";
|
||||
|
||||
pub fn init<S: Storage, A: Api, Q: Querier>(
|
||||
deps: &mut Extern<S, A, Q>,
|
||||
env: Env,
|
||||
|
@ -53,9 +41,8 @@ pub fn init<S: Storage, A: Api, Q: Querier>(
|
|||
let state = ConfigInfo {
|
||||
guardian_set_index: 0,
|
||||
guardian_set_expirity: msg.guardian_set_expirity,
|
||||
wrapped_asset_code_id: msg.wrapped_asset_code_id,
|
||||
owner: deps.api.canonical_address(&env.message.sender)?,
|
||||
fee: Coin::new(FEE_AMOUNT, FEE_DENOMINATION), // 0.01 Luna (or 10000 uluna) fee by default
|
||||
fee: Coin::new(FEE_AMOUNT, FEE_DENOMINATION), // 0.01 Luna (or 10000 uluna) fee by default
|
||||
};
|
||||
config(&mut deps.storage).save(&state)?;
|
||||
|
||||
|
@ -75,26 +62,14 @@ pub fn handle<S: Storage, A: Api, Q: Querier>(
|
|||
msg: HandleMsg,
|
||||
) -> StdResult<HandleResponse> {
|
||||
match msg {
|
||||
HandleMsg::SubmitVAA { vaa } => handle_submit_vaa(deps, env, &vaa.as_slice()),
|
||||
HandleMsg::RegisterAssetHook { asset_id } => {
|
||||
handle_register_asset(deps, env, &asset_id.as_slice())
|
||||
HandleMsg::PostMessage { message, nonce } => {
|
||||
handle_post_message(deps, env, &message.as_slice(), nonce)
|
||||
}
|
||||
HandleMsg::LockAssets {
|
||||
asset,
|
||||
recipient,
|
||||
amount,
|
||||
target_chain,
|
||||
nonce,
|
||||
} => handle_lock_assets(
|
||||
deps,
|
||||
env,
|
||||
asset,
|
||||
amount,
|
||||
recipient.as_slice(),
|
||||
target_chain,
|
||||
nonce,
|
||||
),
|
||||
HandleMsg::TransferFee { amount, recipient } => handle_transfer_fee(deps, env, amount, recipient),
|
||||
// HandleMsg::SubmitVAA { vaa } => handle_submit_vaa(deps, env, &vaa.as_slice()),
|
||||
HandleMsg::TransferFee { amount, recipient } => {
|
||||
handle_transfer_fee(deps, env, amount, recipient)
|
||||
}
|
||||
HandleMsg::SubmitVAA { vaa } => handle_submit_vaa(deps, env, vaa.as_slice()),
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -105,17 +80,24 @@ fn handle_submit_vaa<S: Storage, A: Api, Q: Querier>(
|
|||
data: &[u8],
|
||||
) -> StdResult<HandleResponse> {
|
||||
let state = config_read(&deps.storage).load()?;
|
||||
|
||||
let vaa = parse_and_verify_vaa(&deps.storage, data, env.block.time)?;
|
||||
|
||||
let result = match vaa.action {
|
||||
0x01 => {
|
||||
let vaa = parse_and_verify_vaa(&deps.storage, data, env.block.time)?;
|
||||
if vaa.emitter_chain != 0u16 {
|
||||
// chain 0 is the wormhole chain ?
|
||||
return Err(StdError::generic_err(
|
||||
"governance actions may only come from chain 0",
|
||||
));
|
||||
}
|
||||
|
||||
let gov = WormholeGovernance::deserialize(&vaa.payload)?;
|
||||
|
||||
let result = match gov.action {
|
||||
0u8 => {
|
||||
if vaa.guardian_set_index != state.guardian_set_index {
|
||||
return ContractError::NotCurrentGuardianSet.std_err();
|
||||
}
|
||||
vaa_update_guardian_set(deps, env, vaa.payload.as_slice())
|
||||
vaa_update_guardian_set(deps, env, gov.payload.as_slice())
|
||||
}
|
||||
0x10 => vaa_transfer(deps, env, vaa.payload.as_slice()),
|
||||
_ => ContractError::InvalidVAAAction.std_err(),
|
||||
};
|
||||
|
||||
|
@ -152,7 +134,7 @@ fn parse_and_verify_vaa<S: Storage>(
|
|||
if guardian_set.expiration_time != 0 && guardian_set.expiration_time < block_time {
|
||||
return ContractError::GuardianSetExpired.std_err();
|
||||
}
|
||||
if vaa.len_signers < guardian_set.quorum() {
|
||||
if (vaa.len_signers as usize) < guardian_set.quorum() {
|
||||
return ContractError::NoQuorum.std_err();
|
||||
}
|
||||
|
||||
|
@ -197,37 +179,6 @@ fn parse_and_verify_vaa<S: Storage>(
|
|||
Ok(vaa)
|
||||
}
|
||||
|
||||
/// Handle wrapped asset registration messages
|
||||
fn handle_register_asset<S: Storage, A: Api, Q: Querier>(
|
||||
deps: &mut Extern<S, A, Q>,
|
||||
env: Env,
|
||||
asset_id: &[u8],
|
||||
) -> StdResult<HandleResponse> {
|
||||
let mut bucket = wrapped_asset(&mut deps.storage);
|
||||
let result = bucket.load(asset_id);
|
||||
let result = result.map_err(|_| ContractError::RegistrationForbidden.std())?;
|
||||
if result != HumanAddr::from(WRAPPED_ASSET_UPDATING) {
|
||||
return ContractError::AssetAlreadyRegistered.std_err();
|
||||
}
|
||||
|
||||
bucket.save(asset_id, &env.message.sender)?;
|
||||
|
||||
let contract_address: CanonicalAddr =
|
||||
deps.api.canonical_address(&env.message.sender)?;
|
||||
wrapped_asset_address(&mut deps.storage)
|
||||
.save(contract_address.as_slice(), &asset_id.to_vec())?;
|
||||
|
||||
Ok(HandleResponse {
|
||||
messages: vec![],
|
||||
log: vec![
|
||||
log("action", "register_asset"),
|
||||
log("asset_id", format!("{:?}", asset_id)),
|
||||
log("contract_addr", env.message.sender),
|
||||
],
|
||||
data: None,
|
||||
})
|
||||
}
|
||||
|
||||
fn vaa_update_guardian_set<S: Storage, A: Api, Q: Querier>(
|
||||
deps: &mut Extern<S, A, Q>,
|
||||
env: Env,
|
||||
|
@ -298,227 +249,32 @@ fn vaa_update_guardian_set<S: Storage, A: Api, Q: Querier>(
|
|||
})
|
||||
}
|
||||
|
||||
fn vaa_transfer<S: Storage, A: Api, Q: Querier>(
|
||||
fn handle_post_message<S: Storage, A: Api, Q: Querier>(
|
||||
deps: &mut Extern<S, A, Q>,
|
||||
env: Env,
|
||||
data: &[u8],
|
||||
) -> StdResult<HandleResponse> {
|
||||
/* Payload format:
|
||||
0 uint32 nonce
|
||||
4 uint8 source_chain
|
||||
5 uint8 target_chain
|
||||
6 [32]uint8 source_address
|
||||
38 [32]uint8 target_address
|
||||
70 uint8 token_chain
|
||||
71 [32]uint8 token_address
|
||||
103 uint8 decimals
|
||||
104 uint256 amount */
|
||||
|
||||
const SOURCE_CHAIN_POS: usize = 4;
|
||||
const TARGET_CHAIN_POS: usize = 5;
|
||||
const TARGET_ADDRESS_POS: usize = 38;
|
||||
const TOKEN_CHAIN_POS: usize = 70;
|
||||
const TOKEN_ADDRESS_POS: usize = 71;
|
||||
const DECIMALS_POS: usize = 103;
|
||||
const AMOUNT_POS: usize = 104;
|
||||
const PAYLOAD_LEN: usize = 136;
|
||||
|
||||
if PAYLOAD_LEN > data.len() {
|
||||
return ContractError::InvalidVAA.std_err();
|
||||
}
|
||||
|
||||
let source_chain = data.get_u8(SOURCE_CHAIN_POS);
|
||||
let target_chain = data.get_u8(TARGET_CHAIN_POS);
|
||||
|
||||
let target_address = data.get_address(TARGET_ADDRESS_POS);
|
||||
|
||||
let token_chain = data.get_u8(TOKEN_CHAIN_POS);
|
||||
let (not_supported_amount, amount) = data.get_u256(AMOUNT_POS);
|
||||
|
||||
// Check high 128 bit of amount value to be empty
|
||||
if not_supported_amount != 0 {
|
||||
return ContractError::AmountTooHigh.std_err();
|
||||
}
|
||||
|
||||
// Check if source and target chains are different
|
||||
if source_chain == target_chain {
|
||||
return ContractError::SameSourceAndTarget.std_err();
|
||||
}
|
||||
|
||||
// Check if transfer is incoming
|
||||
if target_chain != CHAIN_ID {
|
||||
return ContractError::WrongTargetChain.std_err();
|
||||
}
|
||||
|
||||
if token_chain != CHAIN_ID {
|
||||
let asset_address = data.get_bytes32(TOKEN_ADDRESS_POS);
|
||||
let asset_id = build_asset_id(token_chain, asset_address);
|
||||
|
||||
let mut messages: Vec<CosmosMsg> = vec![];
|
||||
|
||||
// Check if this asset is already deployed
|
||||
let contract_addr = wrapped_asset_read(&deps.storage).load(&asset_id).ok();
|
||||
let contract_addr = contract_addr.filter(|addr| addr != &HumanAddr::from(WRAPPED_ASSET_UPDATING));
|
||||
|
||||
if let Some(contract_addr) = contract_addr {
|
||||
// Asset already deployed, just mint
|
||||
messages.push(CosmosMsg::Wasm(WasmMsg::Execute {
|
||||
contract_addr,
|
||||
msg: to_binary(&WrappedMsg::Mint {
|
||||
recipient: deps
|
||||
.api
|
||||
.human_address(&target_address)
|
||||
.or_else(|_| ContractError::WrongTargetAddressFormat.std_err())?,
|
||||
amount: Uint128::from(amount),
|
||||
})?,
|
||||
send: vec![],
|
||||
}));
|
||||
} else {
|
||||
// Asset is not deployed yet, deploy and mint
|
||||
wrapped_asset(&mut deps.storage).save(&asset_id, &HumanAddr::from(WRAPPED_ASSET_UPDATING))?;
|
||||
|
||||
let state = config_read(&deps.storage).load()?;
|
||||
messages.push(CosmosMsg::Wasm(WasmMsg::Instantiate {
|
||||
code_id: state.wrapped_asset_code_id,
|
||||
msg: to_binary(&WrappedInit {
|
||||
asset_chain: token_chain,
|
||||
asset_address: asset_address.to_vec().into(),
|
||||
decimals: data.get_u8(DECIMALS_POS),
|
||||
mint: Some(InitMint {
|
||||
recipient: deps
|
||||
.api
|
||||
.human_address(&target_address)
|
||||
.or_else(|_| ContractError::WrongTargetAddressFormat.std_err())?,
|
||||
amount: Uint128::from(amount),
|
||||
}),
|
||||
init_hook: Some(InitHook {
|
||||
contract_addr: env.contract.address,
|
||||
msg: to_binary(&HandleMsg::RegisterAssetHook {
|
||||
asset_id: asset_id.to_vec().into(),
|
||||
})?,
|
||||
}),
|
||||
})?,
|
||||
send: vec![],
|
||||
label: None,
|
||||
}));
|
||||
}
|
||||
|
||||
Ok(HandleResponse {
|
||||
messages,
|
||||
log: vec![],
|
||||
data: None,
|
||||
})
|
||||
} else {
|
||||
let token_address = data.get_address(TOKEN_ADDRESS_POS);
|
||||
|
||||
Ok(HandleResponse {
|
||||
messages: vec![CosmosMsg::Wasm(WasmMsg::Execute {
|
||||
contract_addr: deps.api.human_address(&token_address)?,
|
||||
msg: to_binary(&TokenMsg::Transfer {
|
||||
recipient: deps.api.human_address(&target_address)?,
|
||||
amount: Uint128::from(amount),
|
||||
})?,
|
||||
send: vec![],
|
||||
})],
|
||||
log: vec![],
|
||||
data: None,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
fn handle_lock_assets<S: Storage, A: Api, Q: Querier>(
|
||||
deps: &mut Extern<S, A, Q>,
|
||||
env: Env,
|
||||
asset: HumanAddr,
|
||||
amount: Uint128,
|
||||
recipient: &[u8],
|
||||
target_chain: u8,
|
||||
message: &[u8],
|
||||
nonce: u32,
|
||||
) -> StdResult<HandleResponse> {
|
||||
if target_chain == CHAIN_ID {
|
||||
return ContractError::SameSourceAndTarget.std_err();
|
||||
}
|
||||
|
||||
if amount.is_zero() {
|
||||
return ContractError::AmountTooLow.std_err();
|
||||
}
|
||||
|
||||
let state = config_read(&deps.storage).load()?;
|
||||
|
||||
|
||||
// Check fee
|
||||
if !has_coins(env.message.sent_funds.as_ref(), &state.fee) {
|
||||
return ContractError::FeeTooLow.std_err();
|
||||
}
|
||||
|
||||
let asset_chain: u8;
|
||||
let asset_address: Vec<u8>;
|
||||
|
||||
// Query asset details
|
||||
let request = QueryRequest::<()>::Wasm(WasmQuery::Smart {
|
||||
contract_addr: asset.clone(),
|
||||
msg: to_binary(&TokenQuery::TokenInfo {})?,
|
||||
});
|
||||
let token_info: TokenInfoResponse = deps.querier.custom_query(&request)?;
|
||||
|
||||
let decimals: u8 = token_info.decimals;
|
||||
|
||||
let asset_canonical: CanonicalAddr = deps.api.canonical_address(&asset)?;
|
||||
|
||||
let mut messages: Vec<CosmosMsg> = vec![];
|
||||
|
||||
match wrapped_asset_address_read(&deps.storage).load(asset_canonical.as_slice()) {
|
||||
Ok(_) => {
|
||||
// This is a deployed wrapped asset, burn it
|
||||
messages.push(CosmosMsg::Wasm(WasmMsg::Execute {
|
||||
contract_addr: asset.clone(),
|
||||
msg: to_binary(&WrappedMsg::Burn {
|
||||
account: env.message.sender.clone(),
|
||||
amount,
|
||||
})?,
|
||||
send: vec![],
|
||||
}));
|
||||
let request = QueryRequest::<()>::Wasm(WasmQuery::Smart {
|
||||
contract_addr: asset,
|
||||
msg: to_binary(&WrappedQuery::WrappedAssetInfo {})?,
|
||||
});
|
||||
let wrapped_token_info: WrappedAssetInfoResponse =
|
||||
deps.querier.custom_query(&request)?;
|
||||
asset_chain = wrapped_token_info.asset_chain;
|
||||
asset_address = wrapped_token_info.asset_address.as_slice().to_vec();
|
||||
}
|
||||
Err(_) => {
|
||||
// This is a regular asset, transfer its balance
|
||||
messages.push(CosmosMsg::Wasm(WasmMsg::Execute {
|
||||
contract_addr: asset,
|
||||
msg: to_binary(&TokenMsg::TransferFrom {
|
||||
owner: env.message.sender.clone(),
|
||||
recipient: env.contract.address.clone(),
|
||||
amount,
|
||||
})?,
|
||||
send: vec![],
|
||||
}));
|
||||
asset_address = extend_address_to_32(&asset_canonical);
|
||||
asset_chain = CHAIN_ID;
|
||||
}
|
||||
};
|
||||
|
||||
Ok(HandleResponse {
|
||||
messages,
|
||||
messages: vec![],
|
||||
log: vec![
|
||||
log("locked.target_chain", target_chain),
|
||||
log("locked.token_chain", asset_chain),
|
||||
log("locked.token_decimals", decimals),
|
||||
log("locked.token", hex::encode(asset_address)),
|
||||
log("message.message", hex::encode(message)),
|
||||
log(
|
||||
"locked.sender",
|
||||
"message.sender",
|
||||
hex::encode(extend_address_to_32(
|
||||
&deps.api.canonical_address(&env.message.sender)?,
|
||||
)),
|
||||
),
|
||||
log("locked.recipient", hex::encode(recipient)),
|
||||
log("locked.amount", amount),
|
||||
log("locked.nonce", nonce),
|
||||
log("locked.block_time", env.block.time),
|
||||
log("message.chain_id", CHAIN_ID),
|
||||
log("message.nonce", nonce),
|
||||
log("message.block_time", env.block.time),
|
||||
],
|
||||
data: None,
|
||||
})
|
||||
|
@ -537,7 +293,7 @@ pub fn handle_transfer_fee<S: Storage, A: Api, Q: Querier>(
|
|||
}
|
||||
|
||||
Ok(HandleResponse {
|
||||
messages: vec![CosmosMsg::Bank(BankMsg::Send{
|
||||
messages: vec![CosmosMsg::Bank(BankMsg::Send {
|
||||
from_address: env.contract.address,
|
||||
to_address: recipient,
|
||||
amount: vec![amount],
|
||||
|
@ -553,13 +309,13 @@ pub fn query<S: Storage, A: Api, Q: Querier>(
|
|||
) -> StdResult<Binary> {
|
||||
match msg {
|
||||
QueryMsg::GuardianSetInfo {} => to_binary(&query_guardian_set_info(deps)?),
|
||||
QueryMsg::WrappedRegistry { chain, address } => {
|
||||
to_binary(&query_wrapped_registry(deps, chain, address.as_slice())?)
|
||||
}
|
||||
QueryMsg::VerifyVAA { vaa, block_time } => {
|
||||
to_binary(&query_parse_and_verify_vaa(deps, &vaa.as_slice(), block_time)?)
|
||||
},
|
||||
QueryMsg::VerifyVAA { vaa, block_time } => to_binary(&query_parse_and_verify_vaa(
|
||||
deps,
|
||||
&vaa.as_slice(),
|
||||
block_time,
|
||||
)?),
|
||||
QueryMsg::GetState {} => to_binary(&query_state(deps)?),
|
||||
QueryMsg::QueryAddressHex { address } => to_binary(&query_address_hex(deps, &address)?),
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -575,19 +331,6 @@ pub fn query_guardian_set_info<S: Storage, A: Api, Q: Querier>(
|
|||
Ok(res)
|
||||
}
|
||||
|
||||
pub fn query_wrapped_registry<S: Storage, A: Api, Q: Querier>(
|
||||
deps: &Extern<S, A, Q>,
|
||||
chain: u8,
|
||||
address: &[u8],
|
||||
) -> StdResult<WrappedRegistryResponse> {
|
||||
let asset_id = build_asset_id(chain, address);
|
||||
// Check if this asset is already deployed
|
||||
match wrapped_asset_read(&deps.storage).load(&asset_id) {
|
||||
Ok(address) => Ok(WrappedRegistryResponse { address }),
|
||||
Err(_) => ContractError::AssetNotFound.std_err(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn query_parse_and_verify_vaa<S: Storage, A: Api, Q: Querier>(
|
||||
deps: &Extern<S, A, Q>,
|
||||
data: &[u8],
|
||||
|
@ -596,13 +339,21 @@ pub fn query_parse_and_verify_vaa<S: Storage, A: Api, Q: Querier>(
|
|||
parse_and_verify_vaa(&deps.storage, data, block_time)
|
||||
}
|
||||
|
||||
// returns the hex of the 32 byte address we use for some address on this chain
|
||||
pub fn query_address_hex<S: Storage, A: Api, Q: Querier>(
|
||||
deps: &Extern<S, A, Q>,
|
||||
address: &HumanAddr,
|
||||
) -> StdResult<GetAddressHexResponse> {
|
||||
Ok(GetAddressHexResponse {
|
||||
hex: hex::encode(extend_address_to_32(&deps.api.canonical_address(&address)?)),
|
||||
})
|
||||
}
|
||||
|
||||
pub fn query_state<S: Storage, A: Api, Q: Querier>(
|
||||
deps: &Extern<S, A, Q>,
|
||||
) -> StdResult<GetStateResponse> {
|
||||
let state = config_read(&deps.storage).load()?;
|
||||
let res = GetStateResponse {
|
||||
fee: state.fee,
|
||||
};
|
||||
let res = GetStateResponse { fee: state.fee };
|
||||
Ok(res)
|
||||
}
|
||||
|
||||
|
@ -631,16 +382,6 @@ fn keys_equal(a: &VerifyKey, b: &GuardianAddress) -> bool {
|
|||
true
|
||||
}
|
||||
|
||||
fn build_asset_id(chain: u8, address: &[u8]) -> Vec<u8> {
|
||||
let mut asset_id: Vec<u8> = vec![];
|
||||
asset_id.push(chain);
|
||||
asset_id.extend_from_slice(address);
|
||||
|
||||
let mut hasher = Keccak256::new();
|
||||
hasher.update(asset_id);
|
||||
hasher.finalize().to_vec()
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
@ -1009,7 +750,7 @@ mod tests {
|
|||
const LOCK_AMOUNT: u128 = 10000000000;
|
||||
const LOCK_RECIPIENT: &str = "0000000000000000000011223344556677889900";
|
||||
const LOCK_TARGET: u8 = 1;
|
||||
const LOCK_WRAPPED_CHAIN: u8 = 2;
|
||||
const LOCK_WRAPPED_CHAIN: u16 = 2;
|
||||
const LOCK_WRAPPED_ASSET: &str = "112233445566ff";
|
||||
const LOCKED_DECIMALS: u8 = 11;
|
||||
const ADDRESS_EXTENSION: &str = "000000000000000000000000";
|
||||
|
@ -1096,7 +837,8 @@ mod tests {
|
|||
};
|
||||
do_init_with_guardians(&mut deps, 1);
|
||||
|
||||
let result = submit_msg_with_fee(&mut deps, MSG_LOCK.clone(), Coin::new(10000, "uluna")).unwrap();
|
||||
let result =
|
||||
submit_msg_with_fee(&mut deps, MSG_LOCK.clone(), Coin::new(10000, "uluna")).unwrap();
|
||||
|
||||
let expected_logs = vec![
|
||||
log("locked.target_chain", LOCK_TARGET),
|
||||
|
@ -1160,7 +902,7 @@ mod tests {
|
|||
let register_msg = HandleMsg::RegisterAssetHook {
|
||||
asset_id: Binary::from(LOCK_ASSET_ID),
|
||||
};
|
||||
|
||||
|
||||
let result = submit_msg_with_sender(
|
||||
&mut deps,
|
||||
register_msg.clone(),
|
||||
|
@ -1185,7 +927,9 @@ mod tests {
|
|||
asset_id: Binary::from(LOCK_ASSET_ID),
|
||||
};
|
||||
|
||||
wrapped_asset(&mut deps.storage).save(&LOCK_ASSET_ID, &HumanAddr::from(WRAPPED_ASSET_UPDATING)).unwrap();
|
||||
wrapped_asset(&mut deps.storage)
|
||||
.save(&LOCK_ASSET_ID, &HumanAddr::from(WRAPPED_ASSET_UPDATING))
|
||||
.unwrap();
|
||||
|
||||
let result = submit_msg_with_sender(
|
||||
&mut deps,
|
||||
|
@ -1196,7 +940,8 @@ mod tests {
|
|||
|
||||
assert!(result.is_ok());
|
||||
|
||||
let result = submit_msg_with_fee(&mut deps, MSG_LOCK.clone(), Coin::new(10000, "uluna")).unwrap();
|
||||
let result =
|
||||
submit_msg_with_fee(&mut deps, MSG_LOCK.clone(), Coin::new(10000, "uluna")).unwrap();
|
||||
|
||||
let expected_logs = vec![
|
||||
log("locked.target_chain", LOCK_TARGET),
|
||||
|
@ -1305,7 +1050,13 @@ mod tests {
|
|||
let decoded_vaa: Binary = hex::decode(VAA_VALID_TRANSFER_3_SIGS)
|
||||
.expect("Decoding failed")
|
||||
.into();
|
||||
let result = query(&deps, QueryMsg::VerifyVAA { vaa: decoded_vaa, block_time: env.block.time });
|
||||
let result = query(
|
||||
&deps,
|
||||
QueryMsg::VerifyVAA {
|
||||
vaa: decoded_vaa,
|
||||
block_time: env.block.time,
|
||||
},
|
||||
);
|
||||
|
||||
assert!(result.is_ok());
|
||||
}
|
||||
|
@ -1328,7 +1079,13 @@ mod tests {
|
|||
let decoded_vaa: Binary = hex::decode(VAA_VALID_TRANSFER_3_SIGS)
|
||||
.expect("Decoding failed")
|
||||
.into();
|
||||
let result = query(&deps, QueryMsg::VerifyVAA { vaa: decoded_vaa, block_time: env.block.time });
|
||||
let result = query(
|
||||
&deps,
|
||||
QueryMsg::VerifyVAA {
|
||||
vaa: decoded_vaa,
|
||||
block_time: env.block.time,
|
||||
},
|
||||
);
|
||||
|
||||
assert_eq!(result, ContractError::GuardianSignatureError.std_err());
|
||||
}
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
use cosmwasm_std::{Binary, HumanAddr, Uint128, Coin};
|
||||
use cosmwasm_std::{Binary, HumanAddr, Coin};
|
||||
use schemars::JsonSchema;
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
|
@ -8,7 +8,6 @@ use crate::state::{GuardianAddress, GuardianSetInfo};
|
|||
pub struct InitMsg {
|
||||
pub initial_guardian_set: GuardianSetInfo,
|
||||
pub guardian_set_expirity: u64,
|
||||
pub wrapped_asset_code_id: u64,
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, JsonSchema)]
|
||||
|
@ -17,15 +16,9 @@ pub enum HandleMsg {
|
|||
SubmitVAA {
|
||||
vaa: Binary,
|
||||
},
|
||||
RegisterAssetHook {
|
||||
asset_id: Binary,
|
||||
},
|
||||
LockAssets {
|
||||
asset: HumanAddr,
|
||||
amount: Uint128,
|
||||
recipient: Binary,
|
||||
target_chain: u8,
|
||||
nonce: u32,
|
||||
PostMessage {
|
||||
message: Binary,
|
||||
nonce: u32
|
||||
},
|
||||
TransferFee {
|
||||
amount: Coin,
|
||||
|
@ -37,9 +30,9 @@ pub enum HandleMsg {
|
|||
#[serde(rename_all = "snake_case")]
|
||||
pub enum QueryMsg {
|
||||
GuardianSetInfo {},
|
||||
WrappedRegistry { chain: u8, address: Binary },
|
||||
VerifyVAA { vaa: Binary, block_time: u64 },
|
||||
GetState {},
|
||||
QueryAddressHex { address: HumanAddr }
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, JsonSchema)]
|
||||
|
@ -60,3 +53,9 @@ pub struct WrappedRegistryResponse {
|
|||
pub struct GetStateResponse {
|
||||
pub fee: Coin,
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, JsonSchema)]
|
||||
#[serde(rename_all = "snake_case")]
|
||||
pub struct GetAddressHexResponse {
|
||||
pub hex: String,
|
||||
}
|
||||
|
|
|
@ -26,9 +26,6 @@ pub struct ConfigInfo {
|
|||
// Period for which a guardian set stays active after it has been replaced
|
||||
pub guardian_set_expirity: u64,
|
||||
|
||||
// Code id for wrapped asset contract
|
||||
pub wrapped_asset_code_id: u64,
|
||||
|
||||
// Contract owner address, it can make contract active/inactive
|
||||
pub owner: CanonicalAddr,
|
||||
|
||||
|
@ -39,12 +36,18 @@ pub struct ConfigInfo {
|
|||
// Validator Action Approval(VAA) data
|
||||
#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, JsonSchema)]
|
||||
pub struct ParsedVAA {
|
||||
|
||||
pub version: u8,
|
||||
pub guardian_set_index: u32,
|
||||
pub len_signers: usize,
|
||||
pub hash: Vec<u8>,
|
||||
pub action: u8,
|
||||
pub timestamp: u64,
|
||||
pub nonce: u32,
|
||||
pub len_signers: u8,
|
||||
|
||||
pub emitter_chain: u16,
|
||||
pub emitter_address: Vec<u8>,
|
||||
pub payload: Vec<u8>,
|
||||
|
||||
pub hash: Vec<u8>
|
||||
}
|
||||
|
||||
impl ParsedVAA {
|
||||
|
@ -60,9 +63,12 @@ impl ParsedVAA {
|
|||
1 [65]uint8 signature
|
||||
|
||||
body:
|
||||
0 uint32 unix seconds
|
||||
4 uint8 action
|
||||
5 [payload_size]uint8 payload */
|
||||
0 uint64 timestamp (unix in seconds)
|
||||
8 uint32 nonce
|
||||
12 uint16 emitter_chain
|
||||
14 [32]uint8 emitter_address
|
||||
46 []uint8 payload
|
||||
*/
|
||||
|
||||
pub const HEADER_LEN: usize = 6;
|
||||
pub const SIGNATURE_LEN: usize = 66;
|
||||
|
@ -70,8 +76,10 @@ impl ParsedVAA {
|
|||
pub const GUARDIAN_SET_INDEX_POS: usize = 1;
|
||||
pub const LEN_SIGNER_POS: usize = 5;
|
||||
|
||||
pub const VAA_ACTION_POS: usize = 4;
|
||||
pub const VAA_PAYLOAD_POS: usize = 5;
|
||||
pub const VAA_NONCE_POS: usize = 8;
|
||||
pub const VAA_EMITTER_CHAIN_POS: usize = 12;
|
||||
pub const VAA_EMITTER_ADDRESS_POS: usize = 14;
|
||||
pub const VAA_PAYLOAD_POS: usize = 46;
|
||||
|
||||
// Signature data offsets in the signature block
|
||||
pub const SIG_DATA_POS: usize = 1;
|
||||
|
@ -101,16 +109,23 @@ impl ParsedVAA {
|
|||
if body_offset + Self::VAA_PAYLOAD_POS > data.len() {
|
||||
return ContractError::InvalidVAA.std_err();
|
||||
}
|
||||
let action = data.get_u8(body_offset + Self::VAA_ACTION_POS);
|
||||
let payload = &data[body_offset + Self::VAA_PAYLOAD_POS..];
|
||||
|
||||
let timestamp = data.get_u64(body_offset);
|
||||
let nonce = data.get_u32(body_offset + Self::VAA_NONCE_POS);
|
||||
let emitter_chain = data.get_u16(body_offset + Self::VAA_EMITTER_CHAIN_POS);
|
||||
let emitter_address = data.get_bytes32(body_offset + Self::VAA_EMITTER_ADDRESS_POS).to_vec();
|
||||
let payload = data[body_offset + Self::VAA_PAYLOAD_POS..].to_vec();
|
||||
|
||||
Ok(ParsedVAA {
|
||||
version,
|
||||
guardian_set_index,
|
||||
len_signers,
|
||||
timestamp,
|
||||
nonce,
|
||||
len_signers: len_signers as u8,
|
||||
emitter_chain,
|
||||
emitter_address,
|
||||
payload,
|
||||
hash,
|
||||
action,
|
||||
payload: payload.to_vec(),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
@ -141,6 +156,10 @@ pub struct GuardianSetInfo {
|
|||
|
||||
impl GuardianSetInfo {
|
||||
pub fn quorum(&self) -> usize {
|
||||
// allow quorum of 0 for testing purposes...
|
||||
if self.addresses.len() == 0 {
|
||||
return 0;
|
||||
}
|
||||
((self.addresses.len() * 10 / 3) * 2) / 10 + 1
|
||||
}
|
||||
}
|
||||
|
@ -165,11 +184,11 @@ pub fn guardian_set_set<S: Storage>(
|
|||
index: u32,
|
||||
data: &GuardianSetInfo,
|
||||
) -> StdResult<()> {
|
||||
bucket(GUARDIAN_SET_KEY, storage).save(&index.to_le_bytes(), data)
|
||||
bucket(GUARDIAN_SET_KEY, storage).save(&index.to_be_bytes(), data)
|
||||
}
|
||||
|
||||
pub fn guardian_set_get<S: Storage>(storage: &S, index: u32) -> StdResult<GuardianSetInfo> {
|
||||
bucket_read(GUARDIAN_SET_KEY, storage).load(&index.to_le_bytes())
|
||||
bucket_read(GUARDIAN_SET_KEY, storage).load(&index.to_be_bytes())
|
||||
}
|
||||
|
||||
pub fn vaa_archive_add<S: Storage>(storage: &mut S, hash: &[u8]) -> StdResult<()> {
|
||||
|
@ -199,6 +218,25 @@ pub fn wrapped_asset_address_read<S: Storage>(storage: &S) -> ReadonlyBucket<S,
|
|||
bucket_read(WRAPPED_ASSET_ADDRESS_KEY, storage)
|
||||
}
|
||||
|
||||
|
||||
pub struct WormholeGovernance {
|
||||
pub action: u8,
|
||||
pub payload: Vec<u8>,
|
||||
}
|
||||
|
||||
impl WormholeGovernance {
|
||||
pub fn deserialize(data: &Vec<u8>) -> StdResult<Self> {
|
||||
let data = data.as_slice();
|
||||
let action = data.get_u8(0);
|
||||
let payload = &data[1..];
|
||||
|
||||
Ok(WormholeGovernance {
|
||||
action,
|
||||
payload: payload.to_vec(),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
@ -235,4 +273,10 @@ mod tests {
|
|||
assert_eq!(build_guardian_set(25).quorum(), 17);
|
||||
assert_eq!(build_guardian_set(100).quorum(), 67);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_deserialize() {
|
||||
let x = vec![1u8,0u8,0u8,0u8,1u8,0u8,0u8,0u8,0u8,0u8,96u8,180u8,80u8,111u8,0u8,0u8,0u8,1u8,0u8,3u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,120u8,73u8,153u8,19u8,90u8,170u8,138u8,60u8,165u8,145u8,68u8,104u8,133u8,47u8,221u8,219u8,221u8,216u8,120u8,157u8,0u8,91u8,48u8,44u8,48u8,44u8,51u8,44u8,48u8,44u8,48u8,44u8,48u8,44u8,48u8,44u8,48u8,44u8,48u8,44u8,48u8,44u8,48u8,44u8,48u8,44u8,48u8,44u8,48u8,44u8,48u8,44u8,53u8,54u8,44u8,50u8,51u8,51u8,44u8,49u8,44u8,49u8,49u8,49u8,44u8,49u8,54u8,55u8,44u8,49u8,57u8,48u8,44u8,50u8,48u8,51u8,44u8,49u8,54u8,44u8,49u8,55u8,54u8,44u8,50u8,49u8,56u8,44u8,50u8,53u8,49u8,44u8,49u8,51u8,49u8,44u8,51u8,57u8,44u8,49u8,54u8,44u8,49u8,57u8,53u8,44u8,50u8,50u8,55u8,44u8,49u8,52u8,57u8,44u8,50u8,51u8,54u8,44u8,49u8,57u8,48u8,44u8,50u8,49u8,50u8,44u8,48u8,44u8,48u8,44u8,48u8,44u8,48u8,44u8,48u8,44u8,48u8,44u8,48u8,44u8,48u8,44u8,48u8,44u8,48u8,44u8,48u8,44u8,48u8,44u8,48u8,44u8,48u8,44u8,48u8,44u8,48u8,44u8,48u8,44u8,48u8,44u8,48u8,44u8,48u8,44u8,48u8,44u8,48u8,44u8,48u8,44u8,48u8,44u8,48u8,44u8,48u8,44u8,48u8,44u8,48u8,44u8,48u8,44u8,48u8,44u8,51u8,44u8,50u8,51u8,50u8,44u8,48u8,44u8,51u8,44u8,48u8,44u8,48u8,44u8,48u8,44u8,48u8,44u8,48u8,44u8,48u8,44u8,48u8,44u8,48u8,44u8,48u8,44u8,48u8,44u8,48u8,44u8,48u8,44u8,53u8,51u8,44u8,49u8,49u8,54u8,44u8,52u8,56u8,44u8,49u8,49u8,54u8,44u8,49u8,52u8,57u8,44u8,49u8,48u8,56u8,44u8,49u8,49u8,51u8,44u8,56u8,44u8,48u8,44u8,50u8,51u8,50u8,44u8,52u8,57u8,44u8,49u8,53u8,50u8,44u8,49u8,44u8,50u8,56u8,44u8,50u8,48u8,51u8,44u8,50u8,49u8,50u8,44u8,50u8,50u8,49u8,44u8,50u8,52u8,49u8,44u8,56u8,53u8,44u8,49u8,48u8,57u8,93u8];
|
||||
ParsedVAA::deserialize(x.as_slice());
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,269 @@
|
|||
from terra_sdk.client.localterra import AsyncLocalTerra
|
||||
from terra_sdk.core.auth import StdFee
|
||||
import asyncio
|
||||
from terra_sdk.core.wasm import (
|
||||
MsgStoreCode,
|
||||
MsgInstantiateContract,
|
||||
MsgExecuteContract,
|
||||
)
|
||||
from terra_sdk.util.contract import get_code_id, get_contract_address, read_file_as_b64
|
||||
import os
|
||||
import base64
|
||||
import pprint
|
||||
|
||||
|
||||
lt = AsyncLocalTerra(gas_prices={"uusd": "0.15"})
|
||||
terra = lt
|
||||
deployer = lt.wallets["test1"]
|
||||
|
||||
sequence = asyncio.get_event_loop().run_until_complete(deployer.sequence())
|
||||
|
||||
|
||||
async def sign_and_broadcast(*msgs):
|
||||
|
||||
global sequence
|
||||
try:
|
||||
tx = await deployer.create_and_sign_tx(
|
||||
msgs=msgs, fee=StdFee(30000000, "20000000uusd"), sequence=sequence
|
||||
)
|
||||
result = await terra.tx.broadcast(tx)
|
||||
sequence += 1
|
||||
if result.is_tx_error():
|
||||
raise Exception(result.raw_log)
|
||||
return result
|
||||
except:
|
||||
sequence = await deployer.sequence()
|
||||
raise
|
||||
|
||||
|
||||
async def store_contract(contract_name):
|
||||
parent_dir = os.path.dirname(__file__)
|
||||
contract_bytes = read_file_as_b64(f"{parent_dir}/artifacts/{contract_name}.wasm")
|
||||
store_code = MsgStoreCode(deployer.key.acc_address, contract_bytes)
|
||||
|
||||
result = await sign_and_broadcast(store_code)
|
||||
code_id = get_code_id(result)
|
||||
print(f"Code id for {contract_name} is {code_id}")
|
||||
return code_id
|
||||
|
||||
|
||||
async def store_contracts():
|
||||
|
||||
parent_dir = os.path.dirname(__file__)
|
||||
contract_names = [
|
||||
i[:-5] for i in os.listdir(f"{parent_dir}/artifacts") if i.endswith(".wasm")
|
||||
]
|
||||
|
||||
return {
|
||||
contract_name: await store_contract(contract_name)
|
||||
for contract_name in contract_names
|
||||
}
|
||||
|
||||
|
||||
class ContractQuerier:
|
||||
def __init__(self, address):
|
||||
self.address = address
|
||||
|
||||
def __getattr__(self, item):
|
||||
async def result_fxn(**kwargs):
|
||||
kwargs = convert_contracts_to_addr(kwargs)
|
||||
return await terra.wasm.contract_query(self.address, {item: kwargs})
|
||||
|
||||
return result_fxn
|
||||
|
||||
|
||||
class Contract:
|
||||
@staticmethod
|
||||
async def create(code_id, **kwargs):
|
||||
kwargs = convert_contracts_to_addr(kwargs)
|
||||
instantiate = MsgInstantiateContract(deployer.key.acc_address, code_id, kwargs)
|
||||
result = await sign_and_broadcast(instantiate)
|
||||
return Contract(get_contract_address(result))
|
||||
|
||||
def __init__(self, address):
|
||||
self.address = address
|
||||
|
||||
def __getattr__(self, item):
|
||||
async def result_fxn(coins=None, **kwargs):
|
||||
kwargs = convert_contracts_to_addr(kwargs)
|
||||
execute = MsgExecuteContract(
|
||||
deployer.key.acc_address, self.address, {item: kwargs}, coins=coins
|
||||
)
|
||||
return await sign_and_broadcast(execute)
|
||||
|
||||
return result_fxn
|
||||
|
||||
@property
|
||||
def query(self):
|
||||
return ContractQuerier(self.address)
|
||||
|
||||
|
||||
def convert_contracts_to_addr(obj):
|
||||
if type(obj) == dict:
|
||||
return {k: convert_contracts_to_addr(v) for k, v in obj.items()}
|
||||
if type(obj) in {list, tuple}:
|
||||
return [convert_contracts_to_addr(i) for i in obj]
|
||||
if type(obj) == Contract:
|
||||
return obj.address
|
||||
return obj
|
||||
|
||||
|
||||
def to_bytes(n, length, byteorder="big"):
|
||||
return int(n).to_bytes(length, byteorder=byteorder)
|
||||
|
||||
|
||||
def assemble_vaa(emitter_chain, emitter_address, payload):
|
||||
import time
|
||||
|
||||
# version, guardian set index, len signatures
|
||||
header = to_bytes(1, 1) + to_bytes(0, 4) + to_bytes(0, 1)
|
||||
# timestamp, nonce, emitter_chain
|
||||
body = to_bytes(time.time(), 8) + to_bytes(1, 4) + to_bytes(emitter_chain, 2)
|
||||
# emitter_address, vaa payload
|
||||
body += emitter_address + payload
|
||||
|
||||
return header + body
|
||||
|
||||
|
||||
async def main():
|
||||
code_ids = await store_contracts()
|
||||
print(code_ids)
|
||||
wormhole = await Contract.create(
|
||||
code_id=code_ids["wormhole"],
|
||||
guardian_set_expirity=10 ** 15,
|
||||
initial_guardian_set={"addresses": [], "expiration_time": 10 ** 15},
|
||||
)
|
||||
|
||||
token_bridge = await Contract.create(
|
||||
code_id=code_ids["token_bridge"],
|
||||
owner=deployer.key.acc_address,
|
||||
wormhole_contract=wormhole,
|
||||
wrapped_asset_code_id=int(code_ids["cw20_wrapped"]),
|
||||
)
|
||||
|
||||
mock_token = await Contract.create(
|
||||
code_id=code_ids["cw20_base"],
|
||||
name="MOCK",
|
||||
symbol="MCK",
|
||||
decimals=6,
|
||||
initial_balances=[{"address": deployer.key.acc_address, "amount": "100000000"}],
|
||||
mint=None,
|
||||
)
|
||||
|
||||
raw_addr = deployer.key.raw_address
|
||||
recipient = b"\0" * 12 + raw_addr
|
||||
recipient = base64.b64encode(recipient)
|
||||
|
||||
print(
|
||||
"Balance before initiate transfer",
|
||||
await mock_token.query.balance(address=deployer.key.acc_address),
|
||||
)
|
||||
|
||||
await mock_token.increase_allowance(spender=token_bridge, amount="1000")
|
||||
bridge_canonical = bytes.fromhex(
|
||||
(await wormhole.query.query_address_hex(address=token_bridge))["hex"]
|
||||
)
|
||||
await token_bridge.register_chain(
|
||||
chain_id=3, chain_address=base64.b64encode(bridge_canonical).decode("utf-8")
|
||||
)
|
||||
|
||||
resp = await token_bridge.initiate_transfer(
|
||||
asset=mock_token,
|
||||
amount="1000",
|
||||
recipient_chain=3,
|
||||
recipient=recipient.decode("utf-8"),
|
||||
nonce=0,
|
||||
coins={"uluna": "10000"},
|
||||
)
|
||||
|
||||
print(
|
||||
"Balance after initiate transfer",
|
||||
await mock_token.query.balance(address=deployer.key.acc_address),
|
||||
)
|
||||
|
||||
logs = resp.logs[0].events_by_type
|
||||
transfer_data = {
|
||||
k: v[0] for k, v in logs["from_contract"].items() if k.startswith("message")
|
||||
}
|
||||
vaa = assemble_vaa(
|
||||
transfer_data["message.chain_id"],
|
||||
bytes.fromhex(transfer_data["message.sender"]),
|
||||
bytes.fromhex(transfer_data["message.message"]),
|
||||
)
|
||||
|
||||
await token_bridge.submit_vaa(data=base64.b64encode(vaa).decode("utf-8"))
|
||||
|
||||
print(
|
||||
"Balance after complete transfer",
|
||||
await mock_token.query.balance(address=deployer.key.acc_address),
|
||||
)
|
||||
|
||||
# pretend there exists another bridge contract with the same address but on solana
|
||||
await token_bridge.register_chain(
|
||||
chain_id=1, chain_address=base64.b64encode(bridge_canonical).decode("utf-8")
|
||||
)
|
||||
|
||||
resp = await token_bridge.create_asset_meta(
|
||||
asset_address=mock_token,
|
||||
nonce=1,
|
||||
coins={"uluna": "10000"},
|
||||
)
|
||||
|
||||
logs = resp.logs[0].events_by_type
|
||||
create_meta_data = {
|
||||
k: v[0] for k, v in logs["from_contract"].items() if k.startswith("message")
|
||||
}
|
||||
message_bytes = bytes.fromhex(create_meta_data["message.message"])
|
||||
|
||||
# switch the chain of the asset meta to say its from solana
|
||||
message_bytes = message_bytes[:1] + to_bytes(1, 2) + message_bytes[3:]
|
||||
vaa = assemble_vaa(
|
||||
1, # totally came from solana
|
||||
bytes.fromhex(create_meta_data["message.sender"]),
|
||||
message_bytes,
|
||||
)
|
||||
|
||||
# attest this metadata and make a wrapped asset from solana
|
||||
resp = await token_bridge.submit_vaa(data=base64.b64encode(vaa).decode("utf-8"))
|
||||
|
||||
wrapped_token = Contract(get_contract_address(resp))
|
||||
|
||||
# now send from solana...
|
||||
message_bytes = bytes.fromhex(transfer_data["message.message"])
|
||||
message_bytes = message_bytes[:1] + to_bytes(1, 2) + message_bytes[3:]
|
||||
|
||||
vaa = assemble_vaa(
|
||||
1, # totally came from solana
|
||||
bytes.fromhex(transfer_data["message.sender"]),
|
||||
message_bytes,
|
||||
)
|
||||
print(
|
||||
"Balance before completing transfer from solana",
|
||||
await wrapped_token.query.balance(address=deployer.key.acc_address),
|
||||
)
|
||||
|
||||
await token_bridge.submit_vaa(data=base64.b64encode(vaa).decode("utf-8"))
|
||||
|
||||
print(
|
||||
"Balance after completing transfer from solana",
|
||||
await wrapped_token.query.balance(address=deployer.key.acc_address),
|
||||
)
|
||||
|
||||
await wrapped_token.increase_allowance(spender=token_bridge, amount="1000")
|
||||
resp = await token_bridge.initiate_transfer(
|
||||
asset=wrapped_token,
|
||||
amount="1000",
|
||||
recipient_chain=1,
|
||||
recipient=recipient.decode("utf-8"),
|
||||
nonce=0,
|
||||
coins={"uluna": "10000"},
|
||||
)
|
||||
|
||||
print(
|
||||
"Balance after completing transfer to solana",
|
||||
await wrapped_token.query.balance(address=deployer.key.acc_address),
|
||||
)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
asyncio.get_event_loop().run_until_complete(main())
|
Loading…
Reference in New Issue