├── .editorconfig ├── .github └── workflows │ ├── ictest-e2e.yml │ ├── release-contracts.yml │ └── rust.yml ├── .gitignore ├── Cargo.lock ├── Cargo.toml ├── LICENSE ├── Makefile ├── NOTICE ├── README.md ├── base_artifacts ├── README.md └── cw20_base.wasm ├── contracts ├── migrate │ ├── .cargo │ │ └── config │ ├── Cargo.toml │ ├── README.md │ └── src │ │ ├── bin │ │ └── schema.rs │ │ ├── contract.rs │ │ ├── error.rs │ │ ├── lib.rs │ │ ├── msg.rs │ │ └── state.rs ├── tf_example │ ├── .cargo │ │ └── config │ ├── Cargo.toml │ └── src │ │ ├── bin │ │ └── schema.rs │ │ ├── contract.rs │ │ ├── error.rs │ │ ├── lib.rs │ │ ├── msg.rs │ │ └── state.rs └── tokenfactory_core │ ├── .cargo │ └── config │ ├── Cargo.toml │ ├── README.md │ └── src │ ├── bin │ └── schema.rs │ ├── contract.rs │ ├── error.rs │ ├── helpers.rs │ ├── lib.rs │ ├── msg.rs │ └── state.rs ├── packages └── tokenfactory-types │ ├── Cargo.toml │ └── src │ ├── lib.rs │ └── msg.rs ├── rustfmt.toml ├── scripts ├── .gitkeep ├── mainnet_upload.sh └── testnet_upload.sh └── test ├── go.mod ├── go.sum ├── helpers ├── cosmwasm.go └── tokenfactory.go └── interchaintest ├── basic_test.go ├── cw20_conversion_test.go ├── native_conversion_test.go ├── query_helpers.go ├── setup.go └── types.go /.editorconfig: -------------------------------------------------------------------------------- 1 | root = true 2 | 3 | [*] 4 | indent_style = space 5 | indent_size = 2 6 | charset = utf-8 7 | trim_trailing_whitespace = true 8 | insert_final_newline = true 9 | 10 | [*.rs] 11 | indent_size = 4 12 | -------------------------------------------------------------------------------- /.github/workflows/ictest-e2e.yml: -------------------------------------------------------------------------------- 1 | name: ictest 2 | 3 | on: [push, pull_request] 4 | 5 | # Cancel task if a new commit is pushed while old workflows run 6 | concurrency: 7 | group: ${{ github.workflow }}-${{ github.ref }} 8 | cancel-in-progress: true 9 | 10 | env: 11 | GO_VERSION: 1.19 12 | 13 | jobs: 14 | e2e-setup: 15 | runs-on: ubuntu-latest 16 | 17 | steps: 18 | # Checkout 19 | - uses: actions/checkout@v3 20 | 21 | # Install Rust 22 | - name: Install latest nightly toolchain 23 | uses: actions-rs/toolchain@v1 24 | with: 25 | profile: minimal 26 | toolchain: nightly 27 | target: wasm32-unknown-unknown 28 | override: true 29 | 30 | # TODO can we cache cosmwasm workspace-optimizer crate.io deps? or does that need its own github ci build 31 | 32 | - name: Build the artifact 33 | run: make compile 34 | 35 | - name: Extract branch name 36 | shell: bash 37 | run: echo "branch=$(echo ${GITHUB_REF#refs/heads/})" | tr '/' '_' >>$GITHUB_OUTPUT 38 | id: extract_branch 39 | 40 | - name: Archive production artifacts 41 | uses: actions/upload-artifact@v3 42 | with: 43 | name: ${{ steps.extract_branch.outputs.branch }}-contracts 44 | retention-days: 1 45 | path: | 46 | artifacts 47 | !artifacts/*.txt 48 | 49 | test-basic: 50 | runs-on: ubuntu-latest 51 | needs: e2e-setup 52 | steps: 53 | - name: checkout chain 54 | uses: actions/checkout@v3 55 | 56 | - name: Extract branch name 57 | shell: bash 58 | run: echo "branch=$(echo ${GITHUB_REF#refs/heads/})" | tr '/' '_' >>$GITHUB_OUTPUT 59 | id: extract_branch 60 | 61 | - name: Setup Golang with cache 62 | uses: magnetikonline/action-golang-cache@v4 63 | with: 64 | go-version: ${{ env.GO_VERSION }} 65 | id: go 66 | 67 | - uses: actions/download-artifact@v3 68 | with: 69 | name: ${{ steps.extract_branch.outputs.branch }}-contracts 70 | path: artifacts 71 | 72 | - run: make ictest-basic 73 | 74 | test-conversion-cw20: 75 | runs-on: ubuntu-latest 76 | needs: e2e-setup 77 | steps: 78 | - name: checkout chain 79 | uses: actions/checkout@v3 80 | 81 | - name: Extract branch name 82 | shell: bash 83 | run: echo "branch=$(echo ${GITHUB_REF#refs/heads/})" | tr '/' '_' >>$GITHUB_OUTPUT 84 | id: extract_branch 85 | 86 | - name: Setup Golang with cache 87 | uses: magnetikonline/action-golang-cache@v4 88 | with: 89 | go-version: ${{ env.GO_VERSION }} 90 | id: go 91 | 92 | - uses: actions/download-artifact@v3 93 | with: 94 | name: ${{ steps.extract_branch.outputs.branch }}-contracts 95 | path: artifacts 96 | 97 | - run: make ictest-conversion-cw20 98 | 99 | test-conversion-native: 100 | runs-on: ubuntu-latest 101 | needs: e2e-setup 102 | steps: 103 | - name: checkout chain 104 | uses: actions/checkout@v3 105 | 106 | - name: Extract branch name 107 | shell: bash 108 | run: echo "branch=$(echo ${GITHUB_REF#refs/heads/})" | tr '/' '_' >>$GITHUB_OUTPUT 109 | id: extract_branch 110 | 111 | - name: Setup Golang with cache 112 | uses: magnetikonline/action-golang-cache@v4 113 | with: 114 | go-version: ${{ env.GO_VERSION }} 115 | id: go 116 | 117 | - uses: actions/download-artifact@v3 118 | with: 119 | name: ${{ steps.extract_branch.outputs.branch }}-contracts 120 | path: artifacts 121 | 122 | - run: make ictest-conversion-native -------------------------------------------------------------------------------- /.github/workflows/release-contracts.yml: -------------------------------------------------------------------------------- 1 | # RefL: https://github.com/DA0-DA0/dao-contracts/pull/640 2 | 3 | name: Release contracts 4 | 5 | permissions: 6 | contents: write 7 | 8 | on: 9 | release: 10 | types: [created] 11 | 12 | jobs: 13 | release: 14 | runs-on: ubuntu-latest 15 | container: cosmwasm/workspace-optimizer:0.12.11 16 | steps: 17 | - uses: actions/checkout@v3 18 | 19 | # tar is required for cargo cache 20 | - run: apk add --no-cache tar 21 | 22 | - name: Set up cargo cache 23 | uses: actions/cache@v3 24 | with: 25 | path: | 26 | ~/.cargo/bin/ 27 | ~/.cargo/registry/index/ 28 | ~/.cargo/registry/cache/ 29 | ~/.cargo/git/db/ 30 | target/ 31 | key: ${{ runner.os }}-cargo-${{ hashFiles('**/Cargo.lock') }} 32 | 33 | - name: Compile contracts 34 | timeout-minutes: 30 35 | run: optimize_workspace.sh . 36 | 37 | - name: Upload contracts 38 | uses: actions/upload-artifact@v3 39 | with: 40 | name: contracts 41 | path: artifacts/ 42 | 43 | - name: release 44 | uses: softprops/action-gh-release@v1 45 | if: startsWith(github.ref, 'refs/tags/') 46 | with: 47 | files: artifacts/* 48 | env: 49 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} -------------------------------------------------------------------------------- /.github/workflows/rust.yml: -------------------------------------------------------------------------------- 1 | name: Rust 2 | 3 | on: 4 | push: 5 | branches: [ "main" ] 6 | pull_request: 7 | branches: [ "main" ] 8 | 9 | env: 10 | CARGO_TERM_COLOR: always 11 | 12 | jobs: 13 | build: 14 | 15 | runs-on: ubuntu-latest 16 | 17 | steps: 18 | - uses: actions/checkout@v3 19 | - name: Build 20 | run: cargo build --verbose 21 | - name: Run tests 22 | run: cargo test --verbose 23 | - name: Check formatting 24 | run: cargo fmt -- --check 25 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | target 2 | .idea 3 | packages/*/schema 4 | contracts/*/schema 5 | .vscode 6 | artifacts/ -------------------------------------------------------------------------------- /Cargo.lock: -------------------------------------------------------------------------------- 1 | # This file is automatically @generated by Cargo. 2 | # It is not intended for manual editing. 3 | version = 3 4 | 5 | [[package]] 6 | name = "ahash" 7 | version = "0.7.6" 8 | source = "registry+https://github.com/rust-lang/crates.io-index" 9 | checksum = "fcb51a0695d8f838b1ee009b3fbf66bda078cd64590202a864a8f3e8c4315c47" 10 | dependencies = [ 11 | "getrandom", 12 | "once_cell", 13 | "version_check", 14 | ] 15 | 16 | [[package]] 17 | name = "anyhow" 18 | version = "1.0.75" 19 | source = "registry+https://github.com/rust-lang/crates.io-index" 20 | checksum = "a4668cab20f66d8d020e1fbc0ebe47217433c1b6c8f2040faf858554e394ace6" 21 | 22 | [[package]] 23 | name = "base16ct" 24 | version = "0.1.1" 25 | source = "registry+https://github.com/rust-lang/crates.io-index" 26 | checksum = "349a06037c7bf932dd7e7d1f653678b2038b9ad46a74102f1fc7bd7872678cce" 27 | 28 | [[package]] 29 | name = "base64" 30 | version = "0.13.1" 31 | source = "registry+https://github.com/rust-lang/crates.io-index" 32 | checksum = "9e1b586273c5702936fe7b7d6896644d8be71e6314cfe09d3167c95f712589e8" 33 | 34 | [[package]] 35 | name = "base64ct" 36 | version = "1.6.0" 37 | source = "registry+https://github.com/rust-lang/crates.io-index" 38 | checksum = "8c3c1a368f70d6cf7302d78f8f7093da241fb8e8807c05cc9e51a125895a6d5b" 39 | 40 | [[package]] 41 | name = "block-buffer" 42 | version = "0.9.0" 43 | source = "registry+https://github.com/rust-lang/crates.io-index" 44 | checksum = "4152116fd6e9dadb291ae18fc1ec3575ed6d84c29642d97890f4b4a3417297e4" 45 | dependencies = [ 46 | "generic-array", 47 | ] 48 | 49 | [[package]] 50 | name = "block-buffer" 51 | version = "0.10.4" 52 | source = "registry+https://github.com/rust-lang/crates.io-index" 53 | checksum = "3078c7629b62d3f0439517fa394996acacc5cbc91c5a20d8c658e77abd503a71" 54 | dependencies = [ 55 | "generic-array", 56 | ] 57 | 58 | [[package]] 59 | name = "bnum" 60 | version = "0.7.0" 61 | source = "registry+https://github.com/rust-lang/crates.io-index" 62 | checksum = "845141a4fade3f790628b7daaaa298a25b204fb28907eb54febe5142db6ce653" 63 | 64 | [[package]] 65 | name = "bumpalo" 66 | version = "3.13.0" 67 | source = "registry+https://github.com/rust-lang/crates.io-index" 68 | checksum = "a3e2c3daef883ecc1b5d58c15adae93470a91d425f3532ba1695849656af3fc1" 69 | 70 | [[package]] 71 | name = "byteorder" 72 | version = "1.4.3" 73 | source = "registry+https://github.com/rust-lang/crates.io-index" 74 | checksum = "14c189c53d098945499cdfa7ecc63567cf3886b3332b312a5b4585d8d3a6a610" 75 | 76 | [[package]] 77 | name = "bytes" 78 | version = "1.4.0" 79 | source = "registry+https://github.com/rust-lang/crates.io-index" 80 | checksum = "89b2fd2a0dcf38d7971e2194b6b6eebab45ae01067456a7fd93d5547a61b70be" 81 | 82 | [[package]] 83 | name = "cfg-if" 84 | version = "1.0.0" 85 | source = "registry+https://github.com/rust-lang/crates.io-index" 86 | checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" 87 | 88 | [[package]] 89 | name = "const-oid" 90 | version = "0.9.5" 91 | source = "registry+https://github.com/rust-lang/crates.io-index" 92 | checksum = "28c122c3980598d243d63d9a704629a2d748d101f278052ff068be5a4423ab6f" 93 | 94 | [[package]] 95 | name = "cosmwasm-crypto" 96 | version = "1.3.3" 97 | source = "registry+https://github.com/rust-lang/crates.io-index" 98 | checksum = "871ce1d5a4b00ed1741f84b377eec19fadd81a904a227bc1e268d76539d26f5e" 99 | dependencies = [ 100 | "digest 0.10.7", 101 | "ed25519-zebra", 102 | "k256", 103 | "rand_core 0.6.4", 104 | "thiserror", 105 | ] 106 | 107 | [[package]] 108 | name = "cosmwasm-derive" 109 | version = "1.3.3" 110 | source = "registry+https://github.com/rust-lang/crates.io-index" 111 | checksum = "7ce8b44b45a7c8c6d6f770cd0a51458c2445c7c15b6115e1d215fa35c77b305c" 112 | dependencies = [ 113 | "syn 1.0.109", 114 | ] 115 | 116 | [[package]] 117 | name = "cosmwasm-schema" 118 | version = "1.3.3" 119 | source = "registry+https://github.com/rust-lang/crates.io-index" 120 | checksum = "99222fa0401ee36389550d8a065700380877a2299c3043d24c38d705708c9d9d" 121 | dependencies = [ 122 | "cosmwasm-schema-derive", 123 | "schemars", 124 | "serde", 125 | "serde_json", 126 | "thiserror", 127 | ] 128 | 129 | [[package]] 130 | name = "cosmwasm-schema-derive" 131 | version = "1.3.3" 132 | source = "registry+https://github.com/rust-lang/crates.io-index" 133 | checksum = "4b74eaf9e585ef8e5e3486b240b13ee593cb0f658b5879696937d8c22243d4fb" 134 | dependencies = [ 135 | "proc-macro2", 136 | "quote", 137 | "syn 1.0.109", 138 | ] 139 | 140 | [[package]] 141 | name = "cosmwasm-std" 142 | version = "1.3.3" 143 | source = "registry+https://github.com/rust-lang/crates.io-index" 144 | checksum = "da78abcf059181e8cb01e95e5003cf64fe95dde6c72b3fe37e5cabc75cdba32a" 145 | dependencies = [ 146 | "base64", 147 | "bnum", 148 | "cosmwasm-crypto", 149 | "cosmwasm-derive", 150 | "derivative", 151 | "forward_ref", 152 | "hex", 153 | "schemars", 154 | "serde", 155 | "serde-json-wasm", 156 | "sha2 0.10.7", 157 | "thiserror", 158 | ] 159 | 160 | [[package]] 161 | name = "cosmwasm-storage" 162 | version = "1.3.3" 163 | source = "registry+https://github.com/rust-lang/crates.io-index" 164 | checksum = "b52be0d56b78f502f3acb75e40908a0d04d9f265b6beb0f86b36ec2ece048748" 165 | dependencies = [ 166 | "cosmwasm-std", 167 | "serde", 168 | ] 169 | 170 | [[package]] 171 | name = "cpufeatures" 172 | version = "0.2.9" 173 | source = "registry+https://github.com/rust-lang/crates.io-index" 174 | checksum = "a17b76ff3a4162b0b27f354a0c87015ddad39d35f9c0c36607a3bdd175dde1f1" 175 | dependencies = [ 176 | "libc", 177 | ] 178 | 179 | [[package]] 180 | name = "crypto-bigint" 181 | version = "0.4.9" 182 | source = "registry+https://github.com/rust-lang/crates.io-index" 183 | checksum = "ef2b4b23cddf68b89b8f8069890e8c270d54e2d5fe1b143820234805e4cb17ef" 184 | dependencies = [ 185 | "generic-array", 186 | "rand_core 0.6.4", 187 | "subtle", 188 | "zeroize", 189 | ] 190 | 191 | [[package]] 192 | name = "crypto-common" 193 | version = "0.1.6" 194 | source = "registry+https://github.com/rust-lang/crates.io-index" 195 | checksum = "1bfb12502f3fc46cca1bb51ac28df9d618d813cdc3d2f25b9fe775a34af26bb3" 196 | dependencies = [ 197 | "generic-array", 198 | "typenum", 199 | ] 200 | 201 | [[package]] 202 | name = "curve25519-dalek" 203 | version = "3.2.0" 204 | source = "registry+https://github.com/rust-lang/crates.io-index" 205 | checksum = "0b9fdf9972b2bd6af2d913799d9ebc165ea4d2e65878e329d9c6b372c4491b61" 206 | dependencies = [ 207 | "byteorder", 208 | "digest 0.9.0", 209 | "rand_core 0.5.1", 210 | "subtle", 211 | "zeroize", 212 | ] 213 | 214 | [[package]] 215 | name = "cw-multi-test" 216 | version = "0.15.1" 217 | source = "registry+https://github.com/rust-lang/crates.io-index" 218 | checksum = "e8e81b4a7821d5eeba0d23f737c16027b39a600742ca8c32eb980895ffd270f4" 219 | dependencies = [ 220 | "anyhow", 221 | "cosmwasm-std", 222 | "cosmwasm-storage", 223 | "cw-storage-plus 0.15.1", 224 | "cw-utils 0.15.1", 225 | "derivative", 226 | "itertools", 227 | "prost", 228 | "schemars", 229 | "serde", 230 | "thiserror", 231 | ] 232 | 233 | [[package]] 234 | name = "cw-multi-test" 235 | version = "0.16.5" 236 | source = "registry+https://github.com/rust-lang/crates.io-index" 237 | checksum = "127c7bb95853b8e828bdab97065c81cb5ddc20f7339180b61b2300565aaa99d1" 238 | dependencies = [ 239 | "anyhow", 240 | "cosmwasm-std", 241 | "cw-storage-plus 1.1.0", 242 | "cw-utils 1.0.1", 243 | "derivative", 244 | "itertools", 245 | "k256", 246 | "prost", 247 | "schemars", 248 | "serde", 249 | "thiserror", 250 | ] 251 | 252 | [[package]] 253 | name = "cw-storage-plus" 254 | version = "0.15.1" 255 | source = "registry+https://github.com/rust-lang/crates.io-index" 256 | checksum = "dc6cf70ef7686e2da9ad7b067c5942cd3e88dd9453f7af42f54557f8af300fb0" 257 | dependencies = [ 258 | "cosmwasm-std", 259 | "schemars", 260 | "serde", 261 | ] 262 | 263 | [[package]] 264 | name = "cw-storage-plus" 265 | version = "1.1.0" 266 | source = "registry+https://github.com/rust-lang/crates.io-index" 267 | checksum = "3f0e92a069d62067f3472c62e30adedb4cab1754725c0f2a682b3128d2bf3c79" 268 | dependencies = [ 269 | "cosmwasm-std", 270 | "schemars", 271 | "serde", 272 | ] 273 | 274 | [[package]] 275 | name = "cw-utils" 276 | version = "0.15.1" 277 | source = "registry+https://github.com/rust-lang/crates.io-index" 278 | checksum = "0ae0b69fa7679de78825b4edeeec045066aa2b2c4b6e063d80042e565bb4da5c" 279 | dependencies = [ 280 | "cosmwasm-schema", 281 | "cosmwasm-std", 282 | "cw2 0.15.1", 283 | "schemars", 284 | "semver", 285 | "serde", 286 | "thiserror", 287 | ] 288 | 289 | [[package]] 290 | name = "cw-utils" 291 | version = "1.0.1" 292 | source = "registry+https://github.com/rust-lang/crates.io-index" 293 | checksum = "c80e93d1deccb8588db03945016a292c3c631e6325d349ebb35d2db6f4f946f7" 294 | dependencies = [ 295 | "cosmwasm-schema", 296 | "cosmwasm-std", 297 | "cw2 1.1.0", 298 | "schemars", 299 | "semver", 300 | "serde", 301 | "thiserror", 302 | ] 303 | 304 | [[package]] 305 | name = "cw2" 306 | version = "0.15.1" 307 | source = "registry+https://github.com/rust-lang/crates.io-index" 308 | checksum = "5abb8ecea72e09afff830252963cb60faf945ce6cef2c20a43814516082653da" 309 | dependencies = [ 310 | "cosmwasm-schema", 311 | "cosmwasm-std", 312 | "cw-storage-plus 0.15.1", 313 | "schemars", 314 | "serde", 315 | ] 316 | 317 | [[package]] 318 | name = "cw2" 319 | version = "1.1.0" 320 | source = "registry+https://github.com/rust-lang/crates.io-index" 321 | checksum = "29ac2dc7a55ad64173ca1e0a46697c31b7a5c51342f55a1e84a724da4eb99908" 322 | dependencies = [ 323 | "cosmwasm-schema", 324 | "cosmwasm-std", 325 | "cw-storage-plus 1.1.0", 326 | "schemars", 327 | "serde", 328 | "thiserror", 329 | ] 330 | 331 | [[package]] 332 | name = "cw20" 333 | version = "1.1.0" 334 | source = "registry+https://github.com/rust-lang/crates.io-index" 335 | checksum = "011c45920f8200bd5d32d4fe52502506f64f2f75651ab408054d4cfc75ca3a9b" 336 | dependencies = [ 337 | "cosmwasm-schema", 338 | "cosmwasm-std", 339 | "cw-utils 1.0.1", 340 | "schemars", 341 | "serde", 342 | ] 343 | 344 | [[package]] 345 | name = "der" 346 | version = "0.6.1" 347 | source = "registry+https://github.com/rust-lang/crates.io-index" 348 | checksum = "f1a467a65c5e759bce6e65eaf91cc29f466cdc57cb65777bd646872a8a1fd4de" 349 | dependencies = [ 350 | "const-oid", 351 | "zeroize", 352 | ] 353 | 354 | [[package]] 355 | name = "derivative" 356 | version = "2.2.0" 357 | source = "registry+https://github.com/rust-lang/crates.io-index" 358 | checksum = "fcc3dd5e9e9c0b295d6e1e4d811fb6f157d5ffd784b8d202fc62eac8035a770b" 359 | dependencies = [ 360 | "proc-macro2", 361 | "quote", 362 | "syn 1.0.109", 363 | ] 364 | 365 | [[package]] 366 | name = "digest" 367 | version = "0.9.0" 368 | source = "registry+https://github.com/rust-lang/crates.io-index" 369 | checksum = "d3dd60d1080a57a05ab032377049e0591415d2b31afd7028356dbf3cc6dcb066" 370 | dependencies = [ 371 | "generic-array", 372 | ] 373 | 374 | [[package]] 375 | name = "digest" 376 | version = "0.10.7" 377 | source = "registry+https://github.com/rust-lang/crates.io-index" 378 | checksum = "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292" 379 | dependencies = [ 380 | "block-buffer 0.10.4", 381 | "crypto-common", 382 | "subtle", 383 | ] 384 | 385 | [[package]] 386 | name = "dyn-clone" 387 | version = "1.0.13" 388 | source = "registry+https://github.com/rust-lang/crates.io-index" 389 | checksum = "bbfc4744c1b8f2a09adc0e55242f60b1af195d88596bd8700be74418c056c555" 390 | 391 | [[package]] 392 | name = "ecdsa" 393 | version = "0.14.8" 394 | source = "registry+https://github.com/rust-lang/crates.io-index" 395 | checksum = "413301934810f597c1d19ca71c8710e99a3f1ba28a0d2ebc01551a2daeea3c5c" 396 | dependencies = [ 397 | "der", 398 | "elliptic-curve", 399 | "rfc6979", 400 | "signature", 401 | ] 402 | 403 | [[package]] 404 | name = "ed25519-zebra" 405 | version = "3.1.0" 406 | source = "registry+https://github.com/rust-lang/crates.io-index" 407 | checksum = "7c24f403d068ad0b359e577a77f92392118be3f3c927538f2bb544a5ecd828c6" 408 | dependencies = [ 409 | "curve25519-dalek", 410 | "hashbrown", 411 | "hex", 412 | "rand_core 0.6.4", 413 | "serde", 414 | "sha2 0.9.9", 415 | "zeroize", 416 | ] 417 | 418 | [[package]] 419 | name = "either" 420 | version = "1.9.0" 421 | source = "registry+https://github.com/rust-lang/crates.io-index" 422 | checksum = "a26ae43d7bcc3b814de94796a5e736d4029efb0ee900c12e2d54c993ad1a1e07" 423 | 424 | [[package]] 425 | name = "elliptic-curve" 426 | version = "0.12.3" 427 | source = "registry+https://github.com/rust-lang/crates.io-index" 428 | checksum = "e7bb888ab5300a19b8e5bceef25ac745ad065f3c9f7efc6de1b91958110891d3" 429 | dependencies = [ 430 | "base16ct", 431 | "crypto-bigint", 432 | "der", 433 | "digest 0.10.7", 434 | "ff", 435 | "generic-array", 436 | "group", 437 | "pkcs8", 438 | "rand_core 0.6.4", 439 | "sec1", 440 | "subtle", 441 | "zeroize", 442 | ] 443 | 444 | [[package]] 445 | name = "ff" 446 | version = "0.12.1" 447 | source = "registry+https://github.com/rust-lang/crates.io-index" 448 | checksum = "d013fc25338cc558c5c2cfbad646908fb23591e2404481826742b651c9af7160" 449 | dependencies = [ 450 | "rand_core 0.6.4", 451 | "subtle", 452 | ] 453 | 454 | [[package]] 455 | name = "forward_ref" 456 | version = "1.0.0" 457 | source = "registry+https://github.com/rust-lang/crates.io-index" 458 | checksum = "c8cbd1169bd7b4a0a20d92b9af7a7e0422888bd38a6f5ec29c1fd8c1558a272e" 459 | 460 | [[package]] 461 | name = "generic-array" 462 | version = "0.14.7" 463 | source = "registry+https://github.com/rust-lang/crates.io-index" 464 | checksum = "85649ca51fd72272d7821adaf274ad91c288277713d9c18820d8499a7ff69e9a" 465 | dependencies = [ 466 | "typenum", 467 | "version_check", 468 | ] 469 | 470 | [[package]] 471 | name = "getrandom" 472 | version = "0.2.10" 473 | source = "registry+https://github.com/rust-lang/crates.io-index" 474 | checksum = "be4136b2a15dd319360be1c07d9933517ccf0be8f16bf62a3bee4f0d618df427" 475 | dependencies = [ 476 | "cfg-if", 477 | "js-sys", 478 | "libc", 479 | "wasi", 480 | "wasm-bindgen", 481 | ] 482 | 483 | [[package]] 484 | name = "group" 485 | version = "0.12.1" 486 | source = "registry+https://github.com/rust-lang/crates.io-index" 487 | checksum = "5dfbfb3a6cfbd390d5c9564ab283a0349b9b9fcd46a706c1eb10e0db70bfbac7" 488 | dependencies = [ 489 | "ff", 490 | "rand_core 0.6.4", 491 | "subtle", 492 | ] 493 | 494 | [[package]] 495 | name = "hashbrown" 496 | version = "0.12.3" 497 | source = "registry+https://github.com/rust-lang/crates.io-index" 498 | checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888" 499 | dependencies = [ 500 | "ahash", 501 | ] 502 | 503 | [[package]] 504 | name = "heck" 505 | version = "0.4.1" 506 | source = "registry+https://github.com/rust-lang/crates.io-index" 507 | checksum = "95505c38b4572b2d910cecb0281560f54b440a19336cbbcb27bf6ce6adc6f5a8" 508 | 509 | [[package]] 510 | name = "hex" 511 | version = "0.4.3" 512 | source = "registry+https://github.com/rust-lang/crates.io-index" 513 | checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70" 514 | 515 | [[package]] 516 | name = "hmac" 517 | version = "0.12.1" 518 | source = "registry+https://github.com/rust-lang/crates.io-index" 519 | checksum = "6c49c37c09c17a53d937dfbb742eb3a961d65a994e6bcdcf37e7399d0cc8ab5e" 520 | dependencies = [ 521 | "digest 0.10.7", 522 | ] 523 | 524 | [[package]] 525 | name = "itertools" 526 | version = "0.10.5" 527 | source = "registry+https://github.com/rust-lang/crates.io-index" 528 | checksum = "b0fd2260e829bddf4cb6ea802289de2f86d6a7a690192fbe91b3f46e0f2c8473" 529 | dependencies = [ 530 | "either", 531 | ] 532 | 533 | [[package]] 534 | name = "itoa" 535 | version = "1.0.9" 536 | source = "registry+https://github.com/rust-lang/crates.io-index" 537 | checksum = "af150ab688ff2122fcef229be89cb50dd66af9e01a4ff320cc137eecc9bacc38" 538 | 539 | [[package]] 540 | name = "js-sys" 541 | version = "0.3.64" 542 | source = "registry+https://github.com/rust-lang/crates.io-index" 543 | checksum = "c5f195fe497f702db0f318b07fdd68edb16955aed830df8363d837542f8f935a" 544 | dependencies = [ 545 | "wasm-bindgen", 546 | ] 547 | 548 | [[package]] 549 | name = "juno-tokenfactory-core" 550 | version = "0.0.4" 551 | dependencies = [ 552 | "cosmwasm-schema", 553 | "cosmwasm-std", 554 | "cosmwasm-storage", 555 | "cw-multi-test 0.16.5", 556 | "cw-storage-plus 1.1.0", 557 | "cw2 1.1.0", 558 | "juno-tokenfactory-types", 559 | "schemars", 560 | "serde", 561 | "thiserror", 562 | "token-bindings 0.11.0", 563 | ] 564 | 565 | [[package]] 566 | name = "juno-tokenfactory-types" 567 | version = "0.1.0" 568 | dependencies = [ 569 | "cosmwasm-schema", 570 | "cosmwasm-std", 571 | "schemars", 572 | "serde", 573 | "token-bindings 0.11.0", 574 | ] 575 | 576 | [[package]] 577 | name = "k256" 578 | version = "0.11.6" 579 | source = "registry+https://github.com/rust-lang/crates.io-index" 580 | checksum = "72c1e0b51e7ec0a97369623508396067a486bd0cbed95a2659a4b863d28cfc8b" 581 | dependencies = [ 582 | "cfg-if", 583 | "ecdsa", 584 | "elliptic-curve", 585 | "sha2 0.10.7", 586 | ] 587 | 588 | [[package]] 589 | name = "libc" 590 | version = "0.2.147" 591 | source = "registry+https://github.com/rust-lang/crates.io-index" 592 | checksum = "b4668fb0ea861c1df094127ac5f1da3409a82116a4ba74fca2e58ef927159bb3" 593 | 594 | [[package]] 595 | name = "log" 596 | version = "0.4.20" 597 | source = "registry+https://github.com/rust-lang/crates.io-index" 598 | checksum = "b5e6163cb8c49088c2c36f57875e58ccd8c87c7427f7fbd50ea6710b2f3f2e8f" 599 | 600 | [[package]] 601 | name = "migrate" 602 | version = "0.1.0" 603 | dependencies = [ 604 | "cosmwasm-schema", 605 | "cosmwasm-std", 606 | "cosmwasm-storage", 607 | "cw-multi-test 0.16.5", 608 | "cw-storage-plus 1.1.0", 609 | "cw2 1.1.0", 610 | "cw20", 611 | "getrandom", 612 | "juno-tokenfactory-types", 613 | "schemars", 614 | "serde", 615 | "strum", 616 | "strum_macros", 617 | "thiserror", 618 | "token-bindings-test", 619 | ] 620 | 621 | [[package]] 622 | name = "once_cell" 623 | version = "1.18.0" 624 | source = "registry+https://github.com/rust-lang/crates.io-index" 625 | checksum = "dd8b5dd2ae5ed71462c540258bedcb51965123ad7e7ccf4b9a8cafaa4a63576d" 626 | 627 | [[package]] 628 | name = "opaque-debug" 629 | version = "0.3.0" 630 | source = "registry+https://github.com/rust-lang/crates.io-index" 631 | checksum = "624a8340c38c1b80fd549087862da4ba43e08858af025b236e509b6649fc13d5" 632 | 633 | [[package]] 634 | name = "pkcs8" 635 | version = "0.9.0" 636 | source = "registry+https://github.com/rust-lang/crates.io-index" 637 | checksum = "9eca2c590a5f85da82668fa685c09ce2888b9430e83299debf1f34b65fd4a4ba" 638 | dependencies = [ 639 | "der", 640 | "spki", 641 | ] 642 | 643 | [[package]] 644 | name = "proc-macro2" 645 | version = "1.0.66" 646 | source = "registry+https://github.com/rust-lang/crates.io-index" 647 | checksum = "18fb31db3f9bddb2ea821cde30a9f70117e3f119938b5ee630b7403aa6e2ead9" 648 | dependencies = [ 649 | "unicode-ident", 650 | ] 651 | 652 | [[package]] 653 | name = "prost" 654 | version = "0.9.0" 655 | source = "registry+https://github.com/rust-lang/crates.io-index" 656 | checksum = "444879275cb4fd84958b1a1d5420d15e6fcf7c235fe47f053c9c2a80aceb6001" 657 | dependencies = [ 658 | "bytes", 659 | "prost-derive", 660 | ] 661 | 662 | [[package]] 663 | name = "prost-derive" 664 | version = "0.9.0" 665 | source = "registry+https://github.com/rust-lang/crates.io-index" 666 | checksum = "f9cc1a3263e07e0bf68e96268f37665207b49560d98739662cdfaae215c720fe" 667 | dependencies = [ 668 | "anyhow", 669 | "itertools", 670 | "proc-macro2", 671 | "quote", 672 | "syn 1.0.109", 673 | ] 674 | 675 | [[package]] 676 | name = "quote" 677 | version = "1.0.33" 678 | source = "registry+https://github.com/rust-lang/crates.io-index" 679 | checksum = "5267fca4496028628a95160fc423a33e8b2e6af8a5302579e322e4b520293cae" 680 | dependencies = [ 681 | "proc-macro2", 682 | ] 683 | 684 | [[package]] 685 | name = "rand_core" 686 | version = "0.5.1" 687 | source = "registry+https://github.com/rust-lang/crates.io-index" 688 | checksum = "90bde5296fc891b0cef12a6d03ddccc162ce7b2aff54160af9338f8d40df6d19" 689 | 690 | [[package]] 691 | name = "rand_core" 692 | version = "0.6.4" 693 | source = "registry+https://github.com/rust-lang/crates.io-index" 694 | checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" 695 | dependencies = [ 696 | "getrandom", 697 | ] 698 | 699 | [[package]] 700 | name = "rfc6979" 701 | version = "0.3.1" 702 | source = "registry+https://github.com/rust-lang/crates.io-index" 703 | checksum = "7743f17af12fa0b03b803ba12cd6a8d9483a587e89c69445e3909655c0b9fabb" 704 | dependencies = [ 705 | "crypto-bigint", 706 | "hmac", 707 | "zeroize", 708 | ] 709 | 710 | [[package]] 711 | name = "rustversion" 712 | version = "1.0.14" 713 | source = "registry+https://github.com/rust-lang/crates.io-index" 714 | checksum = "7ffc183a10b4478d04cbbbfc96d0873219d962dd5accaff2ffbd4ceb7df837f4" 715 | 716 | [[package]] 717 | name = "ryu" 718 | version = "1.0.15" 719 | source = "registry+https://github.com/rust-lang/crates.io-index" 720 | checksum = "1ad4cc8da4ef723ed60bced201181d83791ad433213d8c24efffda1eec85d741" 721 | 722 | [[package]] 723 | name = "schemars" 724 | version = "0.8.12" 725 | source = "registry+https://github.com/rust-lang/crates.io-index" 726 | checksum = "02c613288622e5f0c3fdc5dbd4db1c5fbe752746b1d1a56a0630b78fd00de44f" 727 | dependencies = [ 728 | "dyn-clone", 729 | "schemars_derive", 730 | "serde", 731 | "serde_json", 732 | ] 733 | 734 | [[package]] 735 | name = "schemars_derive" 736 | version = "0.8.12" 737 | source = "registry+https://github.com/rust-lang/crates.io-index" 738 | checksum = "109da1e6b197438deb6db99952990c7f959572794b80ff93707d55a232545e7c" 739 | dependencies = [ 740 | "proc-macro2", 741 | "quote", 742 | "serde_derive_internals", 743 | "syn 1.0.109", 744 | ] 745 | 746 | [[package]] 747 | name = "sec1" 748 | version = "0.3.0" 749 | source = "registry+https://github.com/rust-lang/crates.io-index" 750 | checksum = "3be24c1842290c45df0a7bf069e0c268a747ad05a192f2fd7dcfdbc1cba40928" 751 | dependencies = [ 752 | "base16ct", 753 | "der", 754 | "generic-array", 755 | "pkcs8", 756 | "subtle", 757 | "zeroize", 758 | ] 759 | 760 | [[package]] 761 | name = "semver" 762 | version = "1.0.18" 763 | source = "registry+https://github.com/rust-lang/crates.io-index" 764 | checksum = "b0293b4b29daaf487284529cc2f5675b8e57c61f70167ba415a463651fd6a918" 765 | 766 | [[package]] 767 | name = "serde" 768 | version = "1.0.185" 769 | source = "registry+https://github.com/rust-lang/crates.io-index" 770 | checksum = "be9b6f69f1dfd54c3b568ffa45c310d6973a5e5148fd40cf515acaf38cf5bc31" 771 | dependencies = [ 772 | "serde_derive", 773 | ] 774 | 775 | [[package]] 776 | name = "serde-json-wasm" 777 | version = "0.5.1" 778 | source = "registry+https://github.com/rust-lang/crates.io-index" 779 | checksum = "16a62a1fad1e1828b24acac8f2b468971dade7b8c3c2e672bcadefefb1f8c137" 780 | dependencies = [ 781 | "serde", 782 | ] 783 | 784 | [[package]] 785 | name = "serde_derive" 786 | version = "1.0.185" 787 | source = "registry+https://github.com/rust-lang/crates.io-index" 788 | checksum = "dc59dfdcbad1437773485e0367fea4b090a2e0a16d9ffc46af47764536a298ec" 789 | dependencies = [ 790 | "proc-macro2", 791 | "quote", 792 | "syn 2.0.29", 793 | ] 794 | 795 | [[package]] 796 | name = "serde_derive_internals" 797 | version = "0.26.0" 798 | source = "registry+https://github.com/rust-lang/crates.io-index" 799 | checksum = "85bf8229e7920a9f636479437026331ce11aa132b4dde37d121944a44d6e5f3c" 800 | dependencies = [ 801 | "proc-macro2", 802 | "quote", 803 | "syn 1.0.109", 804 | ] 805 | 806 | [[package]] 807 | name = "serde_json" 808 | version = "1.0.105" 809 | source = "registry+https://github.com/rust-lang/crates.io-index" 810 | checksum = "693151e1ac27563d6dbcec9dee9fbd5da8539b20fa14ad3752b2e6d363ace360" 811 | dependencies = [ 812 | "itoa", 813 | "ryu", 814 | "serde", 815 | ] 816 | 817 | [[package]] 818 | name = "sha2" 819 | version = "0.9.9" 820 | source = "registry+https://github.com/rust-lang/crates.io-index" 821 | checksum = "4d58a1e1bf39749807d89cf2d98ac2dfa0ff1cb3faa38fbb64dd88ac8013d800" 822 | dependencies = [ 823 | "block-buffer 0.9.0", 824 | "cfg-if", 825 | "cpufeatures", 826 | "digest 0.9.0", 827 | "opaque-debug", 828 | ] 829 | 830 | [[package]] 831 | name = "sha2" 832 | version = "0.10.7" 833 | source = "registry+https://github.com/rust-lang/crates.io-index" 834 | checksum = "479fb9d862239e610720565ca91403019f2f00410f1864c5aa7479b950a76ed8" 835 | dependencies = [ 836 | "cfg-if", 837 | "cpufeatures", 838 | "digest 0.10.7", 839 | ] 840 | 841 | [[package]] 842 | name = "signature" 843 | version = "1.6.4" 844 | source = "registry+https://github.com/rust-lang/crates.io-index" 845 | checksum = "74233d3b3b2f6d4b006dc19dee745e73e2a6bfb6f93607cd3b02bd5b00797d7c" 846 | dependencies = [ 847 | "digest 0.10.7", 848 | "rand_core 0.6.4", 849 | ] 850 | 851 | [[package]] 852 | name = "spki" 853 | version = "0.6.0" 854 | source = "registry+https://github.com/rust-lang/crates.io-index" 855 | checksum = "67cf02bbac7a337dc36e4f5a693db6c21e7863f45070f7064577eb4367a3212b" 856 | dependencies = [ 857 | "base64ct", 858 | "der", 859 | ] 860 | 861 | [[package]] 862 | name = "strum" 863 | version = "0.24.1" 864 | source = "registry+https://github.com/rust-lang/crates.io-index" 865 | checksum = "063e6045c0e62079840579a7e47a355ae92f60eb74daaf156fb1e84ba164e63f" 866 | 867 | [[package]] 868 | name = "strum_macros" 869 | version = "0.24.3" 870 | source = "registry+https://github.com/rust-lang/crates.io-index" 871 | checksum = "1e385be0d24f186b4ce2f9982191e7101bb737312ad61c1f2f984f34bcf85d59" 872 | dependencies = [ 873 | "heck", 874 | "proc-macro2", 875 | "quote", 876 | "rustversion", 877 | "syn 1.0.109", 878 | ] 879 | 880 | [[package]] 881 | name = "subtle" 882 | version = "2.5.0" 883 | source = "registry+https://github.com/rust-lang/crates.io-index" 884 | checksum = "81cdd64d312baedb58e21336b31bc043b77e01cc99033ce76ef539f78e965ebc" 885 | 886 | [[package]] 887 | name = "syn" 888 | version = "1.0.109" 889 | source = "registry+https://github.com/rust-lang/crates.io-index" 890 | checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237" 891 | dependencies = [ 892 | "proc-macro2", 893 | "quote", 894 | "unicode-ident", 895 | ] 896 | 897 | [[package]] 898 | name = "syn" 899 | version = "2.0.29" 900 | source = "registry+https://github.com/rust-lang/crates.io-index" 901 | checksum = "c324c494eba9d92503e6f1ef2e6df781e78f6a7705a0202d9801b198807d518a" 902 | dependencies = [ 903 | "proc-macro2", 904 | "quote", 905 | "unicode-ident", 906 | ] 907 | 908 | [[package]] 909 | name = "tf-example" 910 | version = "1.0.0" 911 | dependencies = [ 912 | "cosmwasm-schema", 913 | "cosmwasm-std", 914 | "cosmwasm-storage", 915 | "cw-storage-plus 1.1.0", 916 | "juno-tokenfactory-types", 917 | "schemars", 918 | "thiserror", 919 | ] 920 | 921 | [[package]] 922 | name = "thiserror" 923 | version = "1.0.47" 924 | source = "registry+https://github.com/rust-lang/crates.io-index" 925 | checksum = "97a802ec30afc17eee47b2855fc72e0c4cd62be9b4efe6591edde0ec5bd68d8f" 926 | dependencies = [ 927 | "thiserror-impl", 928 | ] 929 | 930 | [[package]] 931 | name = "thiserror-impl" 932 | version = "1.0.47" 933 | source = "registry+https://github.com/rust-lang/crates.io-index" 934 | checksum = "6bb623b56e39ab7dcd4b1b98bb6c8f8d907ed255b18de254088016b27a8ee19b" 935 | dependencies = [ 936 | "proc-macro2", 937 | "quote", 938 | "syn 2.0.29", 939 | ] 940 | 941 | [[package]] 942 | name = "token-bindings" 943 | version = "0.8.0" 944 | source = "git+https://github.com/CosmWasm/token-bindings#0a13213ceadb1f83a702c6b5f0a6bf8b884b3ada" 945 | dependencies = [ 946 | "cosmwasm-schema", 947 | "cosmwasm-std", 948 | "schemars", 949 | "serde", 950 | ] 951 | 952 | [[package]] 953 | name = "token-bindings" 954 | version = "0.11.0" 955 | source = "registry+https://github.com/rust-lang/crates.io-index" 956 | checksum = "9be1c893c90d2993320d9722516ece705460f464616313a62edadb9e71df4502" 957 | dependencies = [ 958 | "cosmwasm-schema", 959 | "cosmwasm-std", 960 | "schemars", 961 | "serde", 962 | ] 963 | 964 | [[package]] 965 | name = "token-bindings-test" 966 | version = "0.8.0" 967 | source = "git+https://github.com/CosmWasm/token-bindings#0a13213ceadb1f83a702c6b5f0a6bf8b884b3ada" 968 | dependencies = [ 969 | "anyhow", 970 | "cosmwasm-std", 971 | "cw-multi-test 0.15.1", 972 | "cw-storage-plus 0.15.1", 973 | "itertools", 974 | "schemars", 975 | "serde", 976 | "thiserror", 977 | "token-bindings 0.8.0", 978 | ] 979 | 980 | [[package]] 981 | name = "typenum" 982 | version = "1.16.0" 983 | source = "registry+https://github.com/rust-lang/crates.io-index" 984 | checksum = "497961ef93d974e23eb6f433eb5fe1b7930b659f06d12dec6fc44a8f554c0bba" 985 | 986 | [[package]] 987 | name = "unicode-ident" 988 | version = "1.0.11" 989 | source = "registry+https://github.com/rust-lang/crates.io-index" 990 | checksum = "301abaae475aa91687eb82514b328ab47a211a533026cb25fc3e519b86adfc3c" 991 | 992 | [[package]] 993 | name = "version_check" 994 | version = "0.9.4" 995 | source = "registry+https://github.com/rust-lang/crates.io-index" 996 | checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f" 997 | 998 | [[package]] 999 | name = "wasi" 1000 | version = "0.11.0+wasi-snapshot-preview1" 1001 | source = "registry+https://github.com/rust-lang/crates.io-index" 1002 | checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" 1003 | 1004 | [[package]] 1005 | name = "wasm-bindgen" 1006 | version = "0.2.87" 1007 | source = "registry+https://github.com/rust-lang/crates.io-index" 1008 | checksum = "7706a72ab36d8cb1f80ffbf0e071533974a60d0a308d01a5d0375bf60499a342" 1009 | dependencies = [ 1010 | "cfg-if", 1011 | "wasm-bindgen-macro", 1012 | ] 1013 | 1014 | [[package]] 1015 | name = "wasm-bindgen-backend" 1016 | version = "0.2.87" 1017 | source = "registry+https://github.com/rust-lang/crates.io-index" 1018 | checksum = "5ef2b6d3c510e9625e5fe6f509ab07d66a760f0885d858736483c32ed7809abd" 1019 | dependencies = [ 1020 | "bumpalo", 1021 | "log", 1022 | "once_cell", 1023 | "proc-macro2", 1024 | "quote", 1025 | "syn 2.0.29", 1026 | "wasm-bindgen-shared", 1027 | ] 1028 | 1029 | [[package]] 1030 | name = "wasm-bindgen-macro" 1031 | version = "0.2.87" 1032 | source = "registry+https://github.com/rust-lang/crates.io-index" 1033 | checksum = "dee495e55982a3bd48105a7b947fd2a9b4a8ae3010041b9e0faab3f9cd028f1d" 1034 | dependencies = [ 1035 | "quote", 1036 | "wasm-bindgen-macro-support", 1037 | ] 1038 | 1039 | [[package]] 1040 | name = "wasm-bindgen-macro-support" 1041 | version = "0.2.87" 1042 | source = "registry+https://github.com/rust-lang/crates.io-index" 1043 | checksum = "54681b18a46765f095758388f2d0cf16eb8d4169b639ab575a8f5693af210c7b" 1044 | dependencies = [ 1045 | "proc-macro2", 1046 | "quote", 1047 | "syn 2.0.29", 1048 | "wasm-bindgen-backend", 1049 | "wasm-bindgen-shared", 1050 | ] 1051 | 1052 | [[package]] 1053 | name = "wasm-bindgen-shared" 1054 | version = "0.2.87" 1055 | source = "registry+https://github.com/rust-lang/crates.io-index" 1056 | checksum = "ca6ad05a4870b2bf5fe995117d3728437bd27d7cd5f06f13c17443ef369775a1" 1057 | 1058 | [[package]] 1059 | name = "zeroize" 1060 | version = "1.6.0" 1061 | source = "registry+https://github.com/rust-lang/crates.io-index" 1062 | checksum = "2a0956f1ba7c7909bfb66c2e9e4124ab6f6482560f6628b5aaeba39207c9aad9" 1063 | -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [workspace] 2 | members = ["contracts/*"] 3 | resolver = "2" 4 | 5 | [profile.release.package.token-reflect] 6 | codegen-units = 1 7 | incremental = false 8 | 9 | [profile.release.package.tokenfactory] 10 | codegen-units = 1 11 | incremental = false 12 | 13 | [profile.release] 14 | rpath = false 15 | lto = true 16 | overflow-checks = true 17 | opt-level = 3 18 | debug = false 19 | debug-assertions = false 20 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | 2 | Apache License 3 | Version 2.0, January 2004 4 | http://www.apache.org/licenses/ 5 | 6 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 7 | 8 | 1. Definitions. 9 | 10 | "License" shall mean the terms and conditions for use, reproduction, 11 | and distribution as defined by Sections 1 through 9 of this document. 12 | 13 | "Licensor" shall mean the copyright owner or entity authorized by 14 | the copyright owner that is granting the License. 15 | 16 | "Legal Entity" shall mean the union of the acting entity and all 17 | other entities that control, are controlled by, or are under common 18 | control with that entity. For the purposes of this definition, 19 | "control" means (i) the power, direct or indirect, to cause the 20 | direction or management of such entity, whether by contract or 21 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 22 | outstanding shares, or (iii) beneficial ownership of such entity. 23 | 24 | "You" (or "Your") shall mean an individual or Legal Entity 25 | exercising permissions granted by this License. 26 | 27 | "Source" form shall mean the preferred form for making modifications, 28 | including but not limited to software source code, documentation 29 | source, and configuration files. 30 | 31 | "Object" form shall mean any form resulting from mechanical 32 | transformation or translation of a Source form, including but 33 | not limited to compiled object code, generated documentation, 34 | and conversions to other media types. 35 | 36 | "Work" shall mean the work of authorship, whether in Source or 37 | Object form, made available under the License, as indicated by a 38 | copyright notice that is included in or attached to the work 39 | (an example is provided in the Appendix below). 40 | 41 | "Derivative Works" shall mean any work, whether in Source or Object 42 | form, that is based on (or derived from) the Work and for which the 43 | editorial revisions, annotations, elaborations, or other modifications 44 | represent, as a whole, an original work of authorship. For the purposes 45 | of this License, Derivative Works shall not include works that remain 46 | separable from, or merely link (or bind by name) to the interfaces of, 47 | the Work and Derivative Works thereof. 48 | 49 | "Contribution" shall mean any work of authorship, including 50 | the original version of the Work and any modifications or additions 51 | to that Work or Derivative Works thereof, that is intentionally 52 | submitted to Licensor for inclusion in the Work by the copyright owner 53 | or by an individual or Legal Entity authorized to submit on behalf of 54 | the copyright owner. For the purposes of this definition, "submitted" 55 | means any form of electronic, verbal, or written communication sent 56 | to the Licensor or its representatives, including but not limited to 57 | communication on electronic mailing lists, source code control systems, 58 | and issue tracking systems that are managed by, or on behalf of, the 59 | Licensor for the purpose of discussing and improving the Work, but 60 | excluding communication that is conspicuously marked or otherwise 61 | designated in writing by the copyright owner as "Not a Contribution." 62 | 63 | "Contributor" shall mean Licensor and any individual or Legal Entity 64 | on behalf of whom a Contribution has been received by Licensor and 65 | subsequently incorporated within the Work. 66 | 67 | 2. Grant of Copyright License. Subject to the terms and conditions of 68 | this License, each Contributor hereby grants to You a perpetual, 69 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 70 | copyright license to reproduce, prepare Derivative Works of, 71 | publicly display, publicly perform, sublicense, and distribute the 72 | Work and such Derivative Works in Source or Object form. 73 | 74 | 3. Grant of Patent License. Subject to the terms and conditions of 75 | this License, each Contributor hereby grants to You a perpetual, 76 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 77 | (except as stated in this section) patent license to make, have made, 78 | use, offer to sell, sell, import, and otherwise transfer the Work, 79 | where such license applies only to those patent claims licensable 80 | by such Contributor that are necessarily infringed by their 81 | Contribution(s) alone or by combination of their Contribution(s) 82 | with the Work to which such Contribution(s) was submitted. If You 83 | institute patent litigation against any entity (including a 84 | cross-claim or counterclaim in a lawsuit) alleging that the Work 85 | or a Contribution incorporated within the Work constitutes direct 86 | or contributory patent infringement, then any patent licenses 87 | granted to You under this License for that Work shall terminate 88 | as of the date such litigation is filed. 89 | 90 | 4. Redistribution. You may reproduce and distribute copies of the 91 | Work or Derivative Works thereof in any medium, with or without 92 | modifications, and in Source or Object form, provided that You 93 | meet the following conditions: 94 | 95 | (a) You must give any other recipients of the Work or 96 | Derivative Works a copy of this License; and 97 | 98 | (b) You must cause any modified files to carry prominent notices 99 | stating that You changed the files; and 100 | 101 | (c) You must retain, in the Source form of any Derivative Works 102 | that You distribute, all copyright, patent, trademark, and 103 | attribution notices from the Source form of the Work, 104 | excluding those notices that do not pertain to any part of 105 | the Derivative Works; and 106 | 107 | (d) If the Work includes a "NOTICE" text file as part of its 108 | distribution, then any Derivative Works that You distribute must 109 | include a readable copy of the attribution notices contained 110 | within such NOTICE file, excluding those notices that do not 111 | pertain to any part of the Derivative Works, in at least one 112 | of the following places: within a NOTICE text file distributed 113 | as part of the Derivative Works; within the Source form or 114 | documentation, if provided along with the Derivative Works; or, 115 | within a display generated by the Derivative Works, if and 116 | wherever such third-party notices normally appear. The contents 117 | of the NOTICE file are for informational purposes only and 118 | do not modify the License. You may add Your own attribution 119 | notices within Derivative Works that You distribute, alongside 120 | or as an addendum to the NOTICE text from the Work, provided 121 | that such additional attribution notices cannot be construed 122 | as modifying the License. 123 | 124 | You may add Your own copyright statement to Your modifications and 125 | may provide additional or different license terms and conditions 126 | for use, reproduction, or distribution of Your modifications, or 127 | for any such Derivative Works as a whole, provided Your use, 128 | reproduction, and distribution of the Work otherwise complies with 129 | the conditions stated in this License. 130 | 131 | 5. Submission of Contributions. Unless You explicitly state otherwise, 132 | any Contribution intentionally submitted for inclusion in the Work 133 | by You to the Licensor shall be under the terms and conditions of 134 | this License, without any additional terms or conditions. 135 | Notwithstanding the above, nothing herein shall supersede or modify 136 | the terms of any separate license agreement you may have executed 137 | with Licensor regarding such Contributions. 138 | 139 | 6. Trademarks. This License does not grant permission to use the trade 140 | names, trademarks, service marks, or product names of the Licensor, 141 | except as required for reasonable and customary use in describing the 142 | origin of the Work and reproducing the content of the NOTICE file. 143 | 144 | 7. Disclaimer of Warranty. Unless required by applicable law or 145 | agreed to in writing, Licensor provides the Work (and each 146 | Contributor provides its Contributions) on an "AS IS" BASIS, 147 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 148 | implied, including, without limitation, any warranties or conditions 149 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 150 | PARTICULAR PURPOSE. You are solely responsible for determining the 151 | appropriateness of using or redistributing the Work and assume any 152 | risks associated with Your exercise of permissions under this License. 153 | 154 | 8. Limitation of Liability. In no event and under no legal theory, 155 | whether in tort (including negligence), contract, or otherwise, 156 | unless required by applicable law (such as deliberate and grossly 157 | negligent acts) or agreed to in writing, shall any Contributor be 158 | liable to You for damages, including any direct, indirect, special, 159 | incidental, or consequential damages of any character arising as a 160 | result of this License or out of the use or inability to use the 161 | Work (including but not limited to damages for loss of goodwill, 162 | work stoppage, computer failure or malfunction, or any and all 163 | other commercial damages or losses), even if such Contributor 164 | has been advised of the possibility of such damages. 165 | 166 | 9. Accepting Warranty or Additional Liability. While redistributing 167 | the Work or Derivative Works thereof, You may choose to offer, 168 | and charge a fee for, acceptance of support, warranty, indemnity, 169 | or other liability obligations and/or rights consistent with this 170 | License. However, in accepting such obligations, You may act only 171 | on Your own behalf and on Your sole responsibility, not on behalf 172 | of any other Contributor, and only if You agree to indemnify, 173 | defend, and hold each Contributor harmless for any liability 174 | incurred by, or claims asserted against, such Contributor by reason 175 | of your accepting any such warranty or additional liability. 176 | 177 | END OF TERMS AND CONDITIONS 178 | 179 | APPENDIX: How to apply the Apache License to your work. 180 | 181 | To apply the Apache License to your work, attach the following 182 | boilerplate notice, with the fields enclosed by brackets "[]" 183 | replaced with your own identifying information. (Don't include 184 | the brackets!) The text should be enclosed in the appropriate 185 | comment syntax for the file format. We also recommend that a 186 | file or class name and description of purpose be included on the 187 | same "printed page" as the copyright notice for easier 188 | identification within third-party archives. 189 | 190 | Copyright [yyyy] [name of copyright owner] 191 | 192 | Licensed under the Apache License, Version 2.0 (the "License"); 193 | you may not use this file except in compliance with the License. 194 | You may obtain a copy of the License at 195 | 196 | http://www.apache.org/licenses/LICENSE-2.0 197 | 198 | Unless required by applicable law or agreed to in writing, software 199 | distributed under the License is distributed on an "AS IS" BASIS, 200 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 201 | See the License for the specific language governing permissions and 202 | limitations under the License. 203 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | BASENAME = $(shell basename $(shell pwd)) 2 | 3 | .PHONY: compile 4 | compile: 5 | docker run --rm -v "$(shell pwd)":/code --mount type=volume,source="$(BASENAME)_cache",target=/code/target --mount type=volume,source=registry_cache,target=/usr/local/cargo/registry cosmwasm/workspace-optimizer:0.12.10 6 | 7 | .PHONY: clippy 8 | clippy: 9 | cargo clippy 10 | cargo fmt 11 | 12 | .PHONY: test 13 | test: 14 | cargo test -- --nocapture 15 | 16 | .PHONY: ictest-basic 17 | ictest-basic: 18 | cd test/interchaintest && go test -race -v -run TestBasicContract . 19 | 20 | .PHONY: ictest-conversion-cw20 21 | ictest-conversion-cw20: 22 | cd test/interchaintest && go test -race -v -run TestCw20ConversionMigrateContract . 23 | 24 | .PHONY: ictest-conversion-native 25 | ictest-conversion-native: 26 | cd test/interchaintest && go test -race -v -run TestNativeConversionMigrateContract . 27 | 28 | -------------------------------------------------------------------------------- /NOTICE: -------------------------------------------------------------------------------- 1 | Tokenfactory Contracts: Useful contracts to use and interact with the tokenfactory module. 2 | Copyright 2023 Juno Network 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | 16 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Tokenfactory Contracts 2 | 3 | - [Core Middleware Contract](./contracts/tokenfactory_core/) 4 | - [Example Usage of Core](./contracts/tf_example/) 5 | - [CW20 & Native Migrate Contract](./contracts/migrate/) 6 | 7 | External 8 | 9 | - [Injective - CW20 Unwrapper](https://github.com/InjectiveLabs/cw20-adapter) 10 | 11 | --- 12 | 13 | Easy Migration UI: https://juno.reece.sh 14 | 15 | Mainnet & Testnet code ids can be found in the releases tab. 16 | -------------------------------------------------------------------------------- /base_artifacts/README.md: -------------------------------------------------------------------------------- 1 | # Compiled Contracts 2 | 3 | - cw20_base -------------------------------------------------------------------------------- /base_artifacts/cw20_base.wasm: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CosmosContracts/tokenfactory-contracts/d823f37582df317476798a448e1ffe786641f62e/base_artifacts/cw20_base.wasm -------------------------------------------------------------------------------- /contracts/migrate/.cargo/config: -------------------------------------------------------------------------------- 1 | [alias] 2 | wasm = "build --release --lib --target wasm32-unknown-unknown" 3 | unit-test = "test --lib" 4 | schema = "run --bin schema" 5 | -------------------------------------------------------------------------------- /contracts/migrate/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "migrate" 3 | version = "0.1.0" 4 | authors = ["Reece "] 5 | edition = "2018" 6 | 7 | exclude = [ 8 | # Those files are rust-optimizer artifacts. You might want to commit them for convenience but they should not be part of the source code publication. 9 | "contract.wasm", 10 | "hash.txt", 11 | ] 12 | 13 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 14 | 15 | [lib] 16 | crate-type = ["cdylib", "rlib"] 17 | 18 | [profile.release] 19 | opt-level = 3 20 | debug = false 21 | rpath = false 22 | lto = true 23 | debug-assertions = false 24 | codegen-units = 1 25 | panic = 'abort' 26 | incremental = false 27 | overflow-checks = true 28 | 29 | [features] 30 | # for more explicit tests, cargo test --features=backtraces 31 | backtraces = ["cosmwasm-std/backtraces"] 32 | # use library feature to disable all instantiate/execute/query exports 33 | library = [] 34 | 35 | [package.metadata.scripts] 36 | optimize = """docker run --rm -v "$(pwd)":/code \ 37 | --mount type=volume,source="$(basename "$(pwd)")_cache",target=/code/target \ 38 | --mount type=volume,source=registry_cache,target=/usr/local/cargo/registry \ 39 | cosmwasm/rust-optimizer:0.12.6 40 | """ 41 | 42 | [dependencies] 43 | cosmwasm-schema = "1.1" 44 | cosmwasm-std = "1.1" 45 | cosmwasm-storage = "1.1" 46 | cw-storage-plus = "1.0.0" 47 | cw2 = "1.0.0" 48 | cw20 = "1.0.0" 49 | schemars = "0.8" 50 | serde = { version = "1.0", default-features = false, features = ["derive"] } 51 | thiserror = "1.0" 52 | getrandom = { version = "0.2", features = ["js"] } 53 | 54 | strum = "0.24" 55 | strum_macros = "0.24" 56 | 57 | # middleware minting contract 58 | juno-tokenfactory-types = { path = "../../packages/tokenfactory-types" } 59 | 60 | [dev-dependencies] 61 | cw-multi-test = "0.16.1" 62 | token-bindings-test = { git = "https://github.com/CosmWasm/token-bindings" } 63 | -------------------------------------------------------------------------------- /contracts/migrate/README.md: -------------------------------------------------------------------------------- 1 | # CW20 & Native Migrations to TokenFactory 2 | 3 | - Send in an old CW20 token and receive the new token-factory native token. 4 | - Deposit a denomination (IBC, another factory token, or JUNO) and receive the new token-factory native token. 5 | 6 | This requires the [TokenFactory Core Contract](https://github.com/CosmosContracts/tokenfactory-contracts). It allows for multiple contracts to mint for a single native token-factory denomination. Once this contract is initialized and the admin token set to the middleware, you are then ready to get this contract set up. 7 | 8 | View documentation on how to set up your TokenFactory token and metadata: TODO. 9 | 10 | --- 11 | 12 | Steps to begin: 13 | 14 | - Initialize the core-middleware & token factory denomination *(The above link has a guide)* 15 | - Initialize this migration contract with the following message 16 | 17 | ```json 18 | // Example: e2e/migrate/test_e2e.sh upload_cw20mint 19 | { 20 | "cw20_token_address":"juno1MyCW20ContractTokenAddress", 21 | "contract_minter_address":"juno1CoreMiddlewareContractAddress", 22 | "tf_denom":"factory/juno1.../token" 23 | } 24 | 25 | // or 26 | { 27 | "burn_denom":"ibc/ABCDEFGHIJKLMNOPQ", 28 | "contract_minter_address":"juno1CoreMiddlewareContractAddress", 29 | "tf_denom":"factory/juno1.../token" 30 | } 31 | ``` 32 | 33 | **NOTE** burn_denom can also be a `factory/juno1.../token` or `ujuno` itself 34 | 35 | - On the core-middleware contract, add this address to the minter whitelist 36 | 37 | ```json 38 | // Core middleware contract 39 | { 40 | "add_whitelist":{"addresses":["juno1MigrateContractAddress"]} 41 | } 42 | ``` 43 | 44 | Your contract is now ready for users to deposit a CW20 or native token. In return, the Middleware contract will mint the new token-factory native token for them at a 1:1 ratio! 45 | 46 | --- 47 | 48 | ## Other Ideas 49 | 50 | 51 | 52 | - CW20 standard contract with a migrate function (BankSend the factory denominations to the contract, upload new CW20-tf-migrate if total CW20 supply <= held tokenfactory, convert all to the new denom) 53 | ^ Will we hit a gas limit issue? since Juno is only 10m per block 54 | 55 | - DAODAO native converts with VoteModule / CW20 wrappers (on the usage of the token in DAODAO, it burns the CW20 and gives the user the native) -------------------------------------------------------------------------------- /contracts/migrate/src/bin/schema.rs: -------------------------------------------------------------------------------- 1 | use cosmwasm_schema::write_api; 2 | use migrate::msg::{ExecuteMsg, InstantiateMsg, QueryMsg}; 3 | 4 | fn main() { 5 | write_api! { 6 | instantiate: InstantiateMsg, 7 | execute: ExecuteMsg, 8 | query: QueryMsg, 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /contracts/migrate/src/contract.rs: -------------------------------------------------------------------------------- 1 | #[cfg(not(feature = "library"))] 2 | use cosmwasm_std::entry_point; 3 | use cosmwasm_std::{ 4 | to_binary, BankMsg, Binary, Coin, CosmosMsg, Deps, DepsMut, Env, MessageInfo, Response, 5 | StdResult, Uint128, WasmMsg, 6 | }; 7 | use cw2::set_contract_version; 8 | use cw20::Cw20ReceiveMsg; 9 | use juno_tokenfactory_types::msg::ExecuteMsg::Mint; 10 | 11 | use crate::error::ContractError; 12 | use crate::msg::{ExecuteMsg, GetConfig, InstantiateMsg, QueryMsg}; 13 | use crate::state::{State, STATE}; 14 | 15 | // version info for migration info 16 | const CONTRACT_NAME: &str = "crates.io:cw20-migrate"; 17 | const CONTRACT_VERSION: &str = env!("CARGO_PKG_VERSION"); 18 | 19 | #[cfg_attr(not(feature = "library"), entry_point)] 20 | pub fn instantiate( 21 | deps: DepsMut, 22 | _env: Env, 23 | _info: MessageInfo, 24 | msg: InstantiateMsg, 25 | ) -> Result { 26 | set_contract_version(deps.storage, CONTRACT_NAME, CONTRACT_VERSION)?; 27 | 28 | let tf_denom = msg.tf_denom.clone(); 29 | let burn_denom = msg.burn_denom.clone(); 30 | 31 | let cw20_token_address: Option = match msg.cw20_token_address { 32 | Some(cw20_token_address) => { 33 | deps.api.addr_validate(&cw20_token_address)?; 34 | Some(cw20_token_address) 35 | } 36 | None => None, 37 | }; 38 | 39 | if cw20_token_address.is_some() && burn_denom.is_some() { 40 | return Err(ContractError::InvalidDenom { 41 | denom: tf_denom, 42 | message: "Cannot set both burn_denom & cw20_token_address".to_string(), 43 | }); 44 | } 45 | 46 | if !tf_denom.starts_with("factory/") { 47 | return Err(ContractError::InvalidDenom { 48 | denom: tf_denom, 49 | message: "Denom must start with 'factory/'".to_string(), 50 | }); 51 | } 52 | 53 | // ensure contract minter address is a valid contract 54 | let contract_minter_address = deps 55 | .api 56 | .addr_validate(&msg.contract_minter_address)? 57 | .to_string(); 58 | 59 | let state = State { 60 | contract_minter_address, 61 | cw20_token_address, 62 | burn_denom, 63 | tf_denom, 64 | }; 65 | STATE.save(deps.storage, &state)?; 66 | 67 | Ok(Response::new().add_attribute("method", "instantiate")) 68 | } 69 | 70 | #[cfg_attr(not(feature = "library"), entry_point)] 71 | pub fn execute( 72 | deps: DepsMut, 73 | _env: Env, 74 | info: MessageInfo, 75 | msg: ExecuteMsg, 76 | ) -> Result { 77 | match msg { 78 | ExecuteMsg::Receive(cw20_msg) => execute_redeem_mint(deps, info, Some(cw20_msg)), 79 | ExecuteMsg::Convert {} => execute_redeem_mint(deps, info, None), 80 | } 81 | } 82 | 83 | fn handle_cw20_burn( 84 | state: &State, 85 | cw20_msg: &Cw20ReceiveMsg, 86 | info: &MessageInfo, 87 | ) -> Result<(CosmosMsg, Uint128), ContractError> { 88 | let cw20_contract = info.sender.to_string(); 89 | let amt = cw20_msg.amount; 90 | 91 | if cw20_contract != state.cw20_token_address.as_deref().unwrap_or_default() { 92 | return Err(ContractError::InvalidCW20Address {}); 93 | } 94 | 95 | Ok(( 96 | WasmMsg::Execute { 97 | contract_addr: cw20_contract, 98 | msg: to_binary(&cw20::Cw20ExecuteMsg::Burn { amount: amt })?, 99 | funds: vec![], 100 | } 101 | .into(), 102 | amt, 103 | )) 104 | } 105 | 106 | fn handle_native(state: &State, info: &MessageInfo) -> Result<(CosmosMsg, Uint128), ContractError> { 107 | if info.funds.is_empty() { 108 | return Err(ContractError::NoFundsSent {}); 109 | } 110 | 111 | if info.funds.len() > 1 { 112 | return Err(ContractError::InvalidDenom { 113 | denom: info.funds.iter().map(|c| c.denom.clone()).collect(), 114 | message: "Only one denom can be sent".to_string(), 115 | }); 116 | } 117 | 118 | let denom = info.funds[0].denom.clone(); 119 | let amt = info.funds[0].amount; 120 | 121 | if denom != state.burn_denom.as_deref().unwrap() { 122 | return Err(ContractError::InvalidDenom { 123 | denom, 124 | message: "This is not the correct denom to get the factory denom".to_string(), 125 | }); 126 | } 127 | 128 | Ok(( 129 | BankMsg::Burn { 130 | amount: info.funds.clone(), 131 | } 132 | .into(), 133 | amt, 134 | )) 135 | } 136 | 137 | pub fn execute_redeem_mint( 138 | deps: DepsMut, 139 | info: MessageInfo, 140 | cw20_msg: Option, 141 | ) -> Result { 142 | let state = STATE.load(deps.storage)?; 143 | 144 | let address_to_mint_to: String; 145 | 146 | let (burn_msg, amount) = if cw20_msg.is_some() { 147 | address_to_mint_to = cw20_msg.clone().unwrap().sender; 148 | handle_cw20_burn(&state, cw20_msg.as_ref().unwrap(), &info)? 149 | } else { 150 | address_to_mint_to = info.sender.to_string(); 151 | handle_native(&state, &info)? 152 | }; 153 | 154 | let mint_payload = Mint { 155 | address: address_to_mint_to, 156 | denom: vec![Coin { 157 | denom: state.tf_denom, 158 | amount, 159 | }], 160 | }; 161 | 162 | Ok(Response::new() 163 | .add_attribute("method", "execute_redeem_mint") 164 | .add_message(burn_msg) 165 | .add_message(WasmMsg::Execute { 166 | contract_addr: state.contract_minter_address, 167 | msg: to_binary(&mint_payload)?, 168 | funds: vec![], 169 | })) 170 | } 171 | 172 | #[cfg_attr(not(feature = "library"), entry_point)] 173 | pub fn query(deps: Deps, _env: Env, msg: QueryMsg) -> StdResult { 174 | match msg { 175 | QueryMsg::GetConfig {} => { 176 | let state = STATE.load(deps.storage)?; 177 | 178 | to_binary(&GetConfig { 179 | contract_minter_address: state.contract_minter_address, 180 | cw20_token_address: state.cw20_token_address, 181 | burn_denom: state.burn_denom, 182 | tf_denom: state.tf_denom, 183 | }) 184 | } 185 | } 186 | } 187 | -------------------------------------------------------------------------------- /contracts/migrate/src/error.rs: -------------------------------------------------------------------------------- 1 | use cosmwasm_std::StdError; 2 | use thiserror::Error; 3 | 4 | #[derive(Error, Debug, PartialEq)] 5 | pub enum ContractError { 6 | #[error("{0}")] 7 | Std(#[from] StdError), 8 | 9 | #[error("Unauthorized")] 10 | Unauthorized {}, 11 | 12 | #[error("Invalid denom: {denom:?} {message:?}")] 13 | InvalidDenom { denom: String, message: String }, 14 | 15 | #[error("this is not an invalid cw20 message")] 16 | InvalidCW20Message {}, 17 | 18 | #[error("invalid cw20 address, does not match with state.")] 19 | InvalidCW20Address {}, 20 | 21 | #[error("{message:?}")] 22 | InvalidMinterAddress { message: String }, 23 | 24 | #[error("You must send funds to this endpoint to use it.")] 25 | NoFundsSent {}, 26 | } 27 | -------------------------------------------------------------------------------- /contracts/migrate/src/lib.rs: -------------------------------------------------------------------------------- 1 | pub mod contract; 2 | mod error; 3 | pub mod msg; 4 | pub mod state; 5 | 6 | pub use crate::error::ContractError; 7 | -------------------------------------------------------------------------------- /contracts/migrate/src/msg.rs: -------------------------------------------------------------------------------- 1 | use cosmwasm_schema::{cw_serde, QueryResponses}; 2 | use cw20::Cw20ReceiveMsg; 3 | 4 | #[cw_serde] 5 | pub struct InstantiateMsg { 6 | pub contract_minter_address: String, // core middleware contract 7 | 8 | /// if not set, must set burn_denom 9 | pub cw20_token_address: Option, 10 | /// if not set, must set cw20_token_address 11 | pub burn_denom: Option, 12 | 13 | pub tf_denom: String, 14 | } 15 | 16 | #[cw_serde] 17 | pub enum ExecuteMsg { 18 | // Receives a CW20 send via the CW20's contract and mints the token 19 | Receive(Cw20ReceiveMsg), 20 | /// Converts a standard denom amount to the new token factory denom in a 1:1 ratio 21 | Convert {}, 22 | } 23 | 24 | #[cw_serde] 25 | #[derive(QueryResponses)] 26 | pub enum QueryMsg { 27 | #[returns(GetConfig)] 28 | GetConfig {}, 29 | } 30 | 31 | // We define a custom struct for each query response 32 | #[cw_serde] 33 | pub struct GetConfig { 34 | pub contract_minter_address: String, 35 | 36 | pub cw20_token_address: Option, 37 | pub burn_denom: Option, 38 | 39 | pub tf_denom: String, 40 | } 41 | -------------------------------------------------------------------------------- /contracts/migrate/src/state.rs: -------------------------------------------------------------------------------- 1 | use schemars::JsonSchema; 2 | use serde::{Deserialize, Serialize}; 3 | 4 | use cw_storage_plus::Item; 5 | 6 | #[derive(Serialize, Deserialize, Clone, Debug, Eq, PartialEq, JsonSchema)] 7 | pub struct State { 8 | pub contract_minter_address: String, 9 | 10 | /// if not set, must set burn_denom 11 | pub cw20_token_address: Option, 12 | /// if not set, must set cw20_token_address 13 | pub burn_denom: Option, 14 | 15 | pub tf_denom: String, 16 | } 17 | 18 | pub const STATE: Item = Item::new("state"); 19 | -------------------------------------------------------------------------------- /contracts/tf_example/.cargo/config: -------------------------------------------------------------------------------- 1 | [alias] 2 | wasm = "build --release --lib --target wasm32-unknown-unknown" 3 | unit-test = "test --lib" 4 | schema = "run --bin schema" 5 | -------------------------------------------------------------------------------- /contracts/tf_example/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "tf-example" 3 | version = "1.0.0" 4 | authors = ["Reece "] 5 | edition = "2018" 6 | 7 | exclude = [ 8 | # Those files are rust-optimizer artifacts. You might want to commit them for convenience but they should not be part of the source code publication. 9 | "contract.wasm", 10 | "hash.txt", 11 | ] 12 | 13 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 14 | 15 | [lib] 16 | crate-type = ["cdylib", "rlib"] 17 | 18 | [profile.release] 19 | opt-level = 3 20 | debug = false 21 | rpath = false 22 | lto = true 23 | debug-assertions = false 24 | codegen-units = 1 25 | panic = 'abort' 26 | incremental = false 27 | overflow-checks = true 28 | 29 | [features] 30 | # for more explicit tests, cargo test --features=backtraces 31 | backtraces = ["cosmwasm-std/backtraces"] 32 | # use library feature to disable all instantiate/execute/query exports 33 | library = [] 34 | 35 | [package.metadata.scripts] 36 | optimize = """docker run --rm -v "$(pwd)":/code \ 37 | --mount type=volume,source="$(basename "$(pwd)")_cache",target=/code/target \ 38 | --mount type=volume,source=registry_cache,target=/usr/local/cargo/registry \ 39 | cosmwasm/rust-optimizer:0.12.6 40 | """ 41 | 42 | [dependencies] 43 | cosmwasm-schema = "1.2.1" 44 | cosmwasm-std = "1.2.1" 45 | cosmwasm-storage = "1.2.1" 46 | cw-storage-plus = "1.0.1" 47 | schemars = "0.8.11" 48 | thiserror = "1.0" 49 | 50 | juno-tokenfactory-types = { path = "../../packages/tokenfactory-types" } 51 | # for your contract, use the following: 52 | # tokenfactory-types = { git = "https://github.com/CosmosContracts/tokenfactory-contracts" } 53 | -------------------------------------------------------------------------------- /contracts/tf_example/src/bin/schema.rs: -------------------------------------------------------------------------------- 1 | use cosmwasm_schema::write_api; 2 | use tf_example::msg::{ExecuteMsg, InstantiateMsg, QueryMsg}; 3 | 4 | fn main() { 5 | write_api! { 6 | instantiate: InstantiateMsg, 7 | execute: ExecuteMsg, 8 | query: QueryMsg, 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /contracts/tf_example/src/contract.rs: -------------------------------------------------------------------------------- 1 | #[cfg(not(feature = "library"))] 2 | use cosmwasm_std::entry_point; 3 | use cosmwasm_std::{ 4 | to_binary, Addr, Binary, Deps, DepsMut, Env, MessageInfo, Response, StdResult, WasmMsg, 5 | }; 6 | 7 | use crate::error::ContractError; 8 | use crate::msg::{ExecuteMsg, InstantiateMsg, QueryMsg}; 9 | use crate::state::{Config, STATE}; 10 | 11 | use juno_tokenfactory_types::msg::ExecuteMsg::Mint; 12 | 13 | #[cfg_attr(not(feature = "library"), entry_point)] 14 | pub fn instantiate( 15 | deps: DepsMut, 16 | _env: Env, 17 | _info: MessageInfo, 18 | msg: InstantiateMsg, 19 | ) -> Result { 20 | let config = if msg.core_factory_address.is_some() { 21 | let core_addr = deps.api.addr_validate(&msg.core_factory_address.unwrap())?; 22 | Config { 23 | core_address: Some(core_addr.to_string()), 24 | } 25 | } else { 26 | Config { core_address: None } 27 | }; 28 | 29 | STATE.save(deps.storage, &config)?; 30 | 31 | Ok(Response::new().add_attribute("method", "instantiate")) 32 | } 33 | 34 | #[cfg_attr(not(feature = "library"), entry_point)] 35 | pub fn execute( 36 | deps: DepsMut, 37 | _env: Env, 38 | _info: MessageInfo, 39 | msg: ExecuteMsg, 40 | ) -> Result { 41 | match msg { 42 | // While this is named MintTokens, think of it as any other message which does something 43 | ExecuteMsg::MintTokens { 44 | core_factory_address, 45 | denoms, 46 | to_address, 47 | } => { 48 | let core_tf_addr: Addr; 49 | if let Some(addr) = core_factory_address { 50 | core_tf_addr = deps.api.addr_validate(&addr)?; 51 | } else { 52 | core_tf_addr = deps 53 | .api 54 | .addr_validate(&STATE.load(deps.storage)?.core_address.unwrap())?; 55 | } 56 | 57 | let payload = Mint { 58 | address: to_address, 59 | denom: denoms, 60 | }; 61 | let wasm_msg = WasmMsg::Execute { 62 | contract_addr: core_tf_addr.to_string(), 63 | msg: to_binary(&payload)?, 64 | funds: vec![], 65 | }; 66 | 67 | Ok(Response::new() 68 | .add_attribute("method", "execute_mint_tokens") 69 | .add_message(wasm_msg)) 70 | } 71 | } 72 | } 73 | 74 | #[cfg_attr(not(feature = "library"), entry_point)] 75 | pub fn query(deps: Deps, _env: Env, msg: QueryMsg) -> StdResult { 76 | match msg { 77 | QueryMsg::GetConfig {} => { 78 | let state = STATE.load(deps.storage)?; 79 | to_binary(&state) 80 | } 81 | } 82 | } 83 | -------------------------------------------------------------------------------- /contracts/tf_example/src/error.rs: -------------------------------------------------------------------------------- 1 | use cosmwasm_std::StdError; 2 | use thiserror::Error; 3 | 4 | #[derive(Error, Debug, PartialEq)] 5 | pub enum ContractError { 6 | #[error("{0}")] 7 | Std(#[from] StdError), 8 | 9 | #[error("Unauthorized")] 10 | Unauthorized {}, 11 | 12 | #[error("Invalid denom: {denom:?} {message:?}")] 13 | InvalidDenom { denom: String, message: String }, 14 | 15 | #[error("Invalid funds")] 16 | InvalidFunds {}, 17 | // #[error("this is not an invalid cw20 message")] 18 | // InvalidCW20Message {}, 19 | 20 | // #[error("invalid cw20 address, does not match with state.")] 21 | // InvalidCW20Address {}, 22 | 23 | // #[error("This contract does not have enough funds to cover {request:?}. It only has {amount:?} currently.")] 24 | // OutOfFunds { request: Uint128, amount: Uint128 }, 25 | } 26 | -------------------------------------------------------------------------------- /contracts/tf_example/src/lib.rs: -------------------------------------------------------------------------------- 1 | pub mod contract; 2 | mod error; 3 | pub mod msg; 4 | pub mod state; 5 | 6 | pub use crate::error::ContractError; 7 | -------------------------------------------------------------------------------- /contracts/tf_example/src/msg.rs: -------------------------------------------------------------------------------- 1 | use cosmwasm_schema::{cw_serde, QueryResponses}; 2 | 3 | use cosmwasm_std::Coin; 4 | 5 | #[cw_serde] 6 | pub struct InstantiateMsg { 7 | // Assuming we handle all the denoms in 1 contract, we put that here. 8 | pub core_factory_address: Option, 9 | } 10 | 11 | #[cw_serde] 12 | pub enum ExecuteMsg { 13 | MintTokens { 14 | core_factory_address: Option, // handled in state.rs now 15 | 16 | denoms: Vec, 17 | // denoms: String, 18 | to_address: String, 19 | }, 20 | } 21 | 22 | #[cw_serde] 23 | #[derive(QueryResponses)] 24 | pub enum QueryMsg { 25 | #[returns(crate::state::Config)] 26 | GetConfig {}, 27 | } 28 | -------------------------------------------------------------------------------- /contracts/tf_example/src/state.rs: -------------------------------------------------------------------------------- 1 | use cosmwasm_schema::cw_serde; 2 | // use cosmwasm_schema::cw_serde; 3 | use cw_storage_plus::Item; 4 | 5 | #[cw_serde] 6 | pub struct Config { 7 | pub core_address: Option, 8 | } 9 | 10 | pub const STATE: Item = Item::new("config"); 11 | -------------------------------------------------------------------------------- /contracts/tokenfactory_core/.cargo/config: -------------------------------------------------------------------------------- 1 | [alias] 2 | wasm = "build --release --lib --target wasm32-unknown-unknown" 3 | unit-test = "test --lib" 4 | schema = "run --bin schema" 5 | -------------------------------------------------------------------------------- /contracts/tokenfactory_core/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "juno-tokenfactory-core" 3 | version = "0.0.4" 4 | description = "The tokenfactory core middleware for the Juno blockchain." 5 | license = "MIT OR Apache-2.0" 6 | authors = ["Reece "] 7 | edition = "2018" 8 | 9 | exclude = [ 10 | # Those files are rust-optimizer artifacts. You might want to commit them for convenience but they should not be part of the source code publication. 11 | "contract.wasm", 12 | "hash.txt", 13 | ] 14 | 15 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 16 | 17 | [lib] 18 | crate-type = ["cdylib", "rlib"] 19 | 20 | [profile.release] 21 | opt-level = 3 22 | debug = false 23 | rpath = false 24 | lto = true 25 | debug-assertions = false 26 | codegen-units = 1 27 | panic = 'abort' 28 | incremental = false 29 | overflow-checks = true 30 | 31 | [features] 32 | # for more explicit tests, cargo test --features=backtraces 33 | backtraces = ["cosmwasm-std/backtraces"] 34 | # use library feature to disable all instantiate/execute/query exports 35 | library = [] 36 | 37 | [package.metadata.scripts] 38 | optimize = """docker run --rm -v "$(pwd)":/code \ 39 | --mount type=volume,source="$(basename "$(pwd)")_cache",target=/code/target \ 40 | --mount type=volume,source=registry_cache,target=/usr/local/cargo/registry \ 41 | cosmwasm/rust-optimizer:0.12.6 42 | """ 43 | 44 | [dependencies] 45 | cosmwasm-schema = "1.1" 46 | cosmwasm-std = "1.1" 47 | cosmwasm-storage = "1.1" 48 | cw-storage-plus = "1.0.0" 49 | token-bindings = "0.11.0" 50 | cw2 = "1.0.0" 51 | schemars = "0.8" 52 | serde = { version = "1.0", default-features = false, features = ["derive"] } 53 | thiserror = "1.0" 54 | 55 | juno-tokenfactory-types = { path = "../../packages/tokenfactory-types", version = "0.1.0"} 56 | 57 | [dev-dependencies] 58 | cw-multi-test = "0.16.1" 59 | # token-bindings-test = { git = "https://github.com/CosmWasm/token-bindings" } 60 | -------------------------------------------------------------------------------- /contracts/tokenfactory_core/README.md: -------------------------------------------------------------------------------- 1 | # TokenFactory Core (middleware) 2 | 3 | ![crates.io](https://img.shields.io/crates/v/juno-tokenfactory-core.svg) 4 | 5 | This is a contract to which you give the admin of your token denomination(s) from the TokenFactory module. Once this contract has that, it allows other contracts you/your DAO controls to mint tokens for your business logic needs (via a WasmMsg). 6 | 7 | This makes it more flexible since multiple contracts can "mint" tokens on behalf of the contract admin :D 8 | 9 | ## Example Use Case 10 | 11 | $RAC has slots & dice contracts. If every game they want to mint 1 RAC for you for playing, both slots and dice would need to be admin of the token-factory token to mint 12 | With this core contract, a single contract is an admin, then the DAO can whitelist both the dice and slots address to mint tokens on its behalf. 13 | 14 | This way to mint $RAC natively, the dice contract would WasmMsg a mint to the core contract, to then give the user that token. 15 | 16 | --- 17 | 18 | ## Rust Dependency 19 | 20 | Add the following to your `Cargo.toml` dependencies for a contract. Then view the Mint section of this document for how to implement it. 21 | 22 | ```toml 23 | [dependencies] 24 | juno-tokenfactory-types = { git = "https://github.com/CosmosContracts/tokenfactory-contracts" } 25 | ``` 26 | 27 | or from crates.io - 28 | 29 | You can view an example of how to use this in the [example contract](https://github.com/CosmosContracts/tokenfactory-contracts/tree/main/contracts/tf_example/src) or see the [e2e test](https://github.com/CosmosContracts/tokenfactory-contracts/blob/main/e2e/core/test_e2e.sh) for a full example in bash. 30 | 31 | --- 32 | 33 | ## Chain Setup 34 | 35 | Mainnet Store Code: TBD 36 | 37 | ```sh 38 | # for uni-6 TESTNET 39 | # update [key] here to be your local wallet's key or your wallet's address 40 | FLAGS="--gas-prices 0.003ujuno --gas auto --gas-adjustment 1.3 --chain-id uni-6 --node https://juno-testnet-rpc.polkachu.com:443 --output json --from [key]" 41 | 42 | # create a tokenfactory denomination via the CLI. 43 | junod tx tokenfactory create-denom abcde $FLAGS 44 | # factory/juno1......./abcde is your new denom 45 | 46 | 47 | # upload this contract (skip if you use the mainnet code) 48 | # junod tx wasm store artifacts/juno_tokenfactory_core.wasm $FLAGS 49 | 50 | # Initialize this contract 51 | # You may want to set this as a normal admin initially before changing its admin to a DAO 52 | junod tx wasm instantiate "###" {"allowed_mint_addresses":[],"denoms":["factory/juno1./abcde"]} --label "tf-middlware" --admin [key] $FLAGS 53 | # Get the middleware contract address here 54 | 55 | # Transfer ownership of the token to the contract 56 | junod tx tokenfactory change-admin factory/juno1./abcde juno1middlewarecontract $FLAGS 57 | 58 | # Ensure the juno1middlewarecontract now has the admin role 59 | junod q tokenfactory denom-authority-metadata factory/juno1./abcde 60 | ``` 61 | 62 | ## How To Contract Mint 63 | 64 | You can then mint tokens via another contract using the following example 65 | 66 | ```rust 67 | // msg.rs - mint on behalf of the core_factory_address 68 | #[cw_serde] 69 | pub enum ExecuteMsg { 70 | MintTokens { 71 | // You could save this in state.rs on initialize. 72 | core_tf_middleware_contract: String, 73 | denoms: Vec, 74 | to_address: String, 75 | }, 76 | } 77 | 78 | // contract.rs - execute 79 | 80 | // Ensure you added the tokenfactory-types dependency 81 | use juno::juno_tokenfactory_types::msg::ExecuteMsg::Mint; 82 | 83 | ExecuteMsg::MintTokens { 84 | core_tf_middleware_contract, 85 | denoms, 86 | to_address, 87 | } => { 88 | let payload = Mint { 89 | address: to_address, 90 | denom: denoms, 91 | }; 92 | let wasm_msg = WasmMsg::Execute { 93 | contract_addr: core_tf_middleware_contract.to_string(), 94 | msg: to_binary(&payload)?, 95 | funds: vec![], 96 | }; 97 | 98 | Ok(Response::new() 99 | .add_attribute("method", "execute_mint_tokens_from_other_contract") 100 | .add_message(wasm_msg)) 101 | } 102 | ``` 103 | -------------------------------------------------------------------------------- /contracts/tokenfactory_core/src/bin/schema.rs: -------------------------------------------------------------------------------- 1 | use cosmwasm_schema::write_api; 2 | use juno_tokenfactory_core::msg::{ExecuteMsg, InstantiateMsg, QueryMsg}; 3 | 4 | fn main() { 5 | write_api! { 6 | instantiate: InstantiateMsg, 7 | execute: ExecuteMsg, 8 | query: QueryMsg, 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /contracts/tokenfactory_core/src/contract.rs: -------------------------------------------------------------------------------- 1 | #[cfg(not(feature = "library"))] 2 | use cosmwasm_std::entry_point; 3 | use cosmwasm_std::{ 4 | to_binary, AllBalanceResponse, BalanceResponse, BankMsg, BankQuery, Binary, Coin, Deps, 5 | DepsMut, Env, MessageInfo, Response, StdResult, 6 | }; 7 | use cw2::set_contract_version; 8 | 9 | use crate::error::ContractError; 10 | use crate::helpers::{ 11 | create_denom_msg, is_contract_manager, is_whitelisted, mint_factory_token_messages, 12 | mint_tokens_msg, pretty_denoms_output, 13 | }; 14 | use crate::msg::{ExecuteMsg, InstantiateMsg, QueryMsg}; 15 | use crate::state::{Config, CONFIG}; 16 | 17 | use token_bindings::TokenFactoryMsg; 18 | 19 | // version info for migration info 20 | const CONTRACT_NAME: &str = "crates.io:tokenfactory-core"; 21 | const CONTRACT_VERSION: &str = env!("CARGO_PKG_VERSION"); 22 | 23 | #[cfg_attr(not(feature = "library"), entry_point)] 24 | pub fn instantiate( 25 | deps: DepsMut, 26 | env: Env, 27 | _info: MessageInfo, 28 | msg: InstantiateMsg, 29 | ) -> Result, ContractError> { 30 | set_contract_version(deps.storage, CONTRACT_NAME, CONTRACT_VERSION)?; 31 | 32 | // Validate existing denoms. 33 | let mut denoms = msg.existing_denoms.unwrap_or_default(); 34 | for d in denoms.iter() { 35 | if !d.starts_with("factory/") { 36 | return Err(ContractError::InvalidDenom { 37 | denom: d.clone(), 38 | message: "Denom must start with 'factory/'".to_string(), 39 | }); 40 | } 41 | } 42 | 43 | // Create new denoms. 44 | let mut new_denom_msgs: Vec = vec![]; 45 | let mut new_mint_msgs: Vec = vec![]; 46 | 47 | if let Some(new_denoms) = msg.new_denoms { 48 | if !new_denoms.is_empty() { 49 | for denom in new_denoms { 50 | let subdenom = denom.symbol.to_lowercase(); 51 | let full_denom = format!("factory/{}/{}", env.contract.address, subdenom); 52 | 53 | // Add creation message. 54 | new_denom_msgs.push(create_denom_msg( 55 | subdenom.clone(), 56 | full_denom.clone(), 57 | denom.clone(), 58 | )); 59 | 60 | // Add initial balance mint messages. 61 | if let Some(initial_balances) = denom.initial_balances { 62 | if !initial_balances.is_empty() { 63 | // Validate addresses. 64 | for initial in initial_balances.iter() { 65 | deps.api.addr_validate(&initial.address)?; 66 | } 67 | 68 | for b in initial_balances { 69 | new_mint_msgs.push(mint_tokens_msg( 70 | b.address.clone(), 71 | full_denom.clone(), 72 | b.amount, 73 | )); 74 | } 75 | } 76 | } 77 | 78 | // Add to existing denoms. 79 | denoms.push(full_denom); 80 | } 81 | } 82 | } 83 | 84 | if denoms.is_empty() { 85 | return Err(ContractError::NoDenomsProvided {}); 86 | } 87 | 88 | let manager = deps 89 | .api 90 | .addr_validate(&msg.manager.unwrap_or_else(|| _info.sender.to_string()))?; 91 | 92 | let config = Config { 93 | manager: manager.to_string(), 94 | allowed_mint_addresses: msg.allowed_mint_addresses, 95 | denoms, 96 | }; 97 | CONFIG.save(deps.storage, &config)?; 98 | 99 | Ok(Response::new() 100 | .add_attribute("method", "instantiate") 101 | .add_messages(new_denom_msgs) 102 | .add_messages(new_mint_msgs)) 103 | } 104 | 105 | #[cfg_attr(not(feature = "library"), entry_point)] 106 | pub fn execute( 107 | deps: DepsMut, 108 | env: Env, 109 | info: MessageInfo, 110 | msg: ExecuteMsg, 111 | ) -> Result, ContractError> { 112 | match msg { 113 | // == ANYONE == 114 | ExecuteMsg::Burn {} => execute_burn(deps, env, info), 115 | 116 | // == WHITELIST == 117 | ExecuteMsg::Mint { address, denom } => execute_mint(deps, info, address, denom), 118 | 119 | // == MANAGER == 120 | ExecuteMsg::BurnFrom { from, denom } => { 121 | let config = CONFIG.load(deps.storage)?; 122 | is_contract_manager(config, info.sender)?; 123 | 124 | let balance = deps.querier.query_all_balances(from.clone())?; 125 | 126 | let mut found = false; 127 | for coin in balance.iter() { 128 | if coin.denom == denom.denom { 129 | found = true; 130 | } 131 | } 132 | 133 | if !found { 134 | return Err(ContractError::InvalidDenom { 135 | denom: denom.denom, 136 | message: "Denom not found in balance".to_string(), 137 | }); 138 | } 139 | 140 | // burn from from_address 141 | let msg: TokenFactoryMsg = TokenFactoryMsg::BurnTokens { 142 | denom: denom.denom.clone(), 143 | amount: denom.amount, 144 | burn_from_address: from, 145 | }; 146 | 147 | Ok(Response::new() 148 | .add_attribute("method", "execute_burn_from") 149 | .add_attribute("denom", denom.denom) 150 | .add_message(msg)) 151 | } 152 | 153 | ExecuteMsg::TransferAdmin { denom, new_address } => { 154 | execute_transfer_admin(deps, info, denom, new_address) 155 | } 156 | 157 | ExecuteMsg::ForceTransfer { from, to, denom } => { 158 | let config = CONFIG.load(deps.storage)?; 159 | is_contract_manager(config, info.sender)?; 160 | 161 | let msg: TokenFactoryMsg = TokenFactoryMsg::ForceTransfer { 162 | denom: denom.denom.clone(), 163 | amount: denom.amount, 164 | from_address: from, 165 | to_address: to, 166 | }; 167 | 168 | Ok(Response::new() 169 | .add_attribute("method", "execute_force_transfer") 170 | .add_attribute("denom", denom.denom) 171 | .add_message(msg)) 172 | } 173 | 174 | ExecuteMsg::SetMetadata { denom, metadata } => { 175 | let config = CONFIG.load(deps.storage)?; 176 | is_contract_manager(config, info.sender)?; 177 | 178 | let msg: TokenFactoryMsg = TokenFactoryMsg::SetMetadata { 179 | denom: denom.clone(), 180 | metadata, 181 | }; 182 | 183 | Ok(Response::new() 184 | .add_attribute("method", "execute_set_metadata") 185 | .add_attribute("denom", denom) 186 | .add_message(msg)) 187 | } 188 | 189 | // Merge these into a modify whitelist 190 | ExecuteMsg::AddWhitelist { addresses } => { 191 | let config = CONFIG.load(deps.storage)?; 192 | is_contract_manager(config.clone(), info.sender)?; 193 | 194 | // add addresses if it is not in config.allowed_mint_addresses 195 | let mut updated = config.allowed_mint_addresses; 196 | for new in addresses { 197 | if !updated.contains(&new) { 198 | updated.push(new); 199 | } 200 | } 201 | 202 | CONFIG.update(deps.storage, |mut config| -> StdResult<_> { 203 | config.allowed_mint_addresses = updated; 204 | Ok(config) 205 | })?; 206 | 207 | Ok(Response::new().add_attribute("method", "add_whitelist")) 208 | } 209 | ExecuteMsg::RemoveWhitelist { addresses } => { 210 | let config = CONFIG.load(deps.storage)?; 211 | is_contract_manager(config.clone(), info.sender)?; 212 | 213 | let mut updated = config.allowed_mint_addresses; 214 | for remove in addresses { 215 | updated.retain(|a| a != &remove); 216 | } 217 | 218 | CONFIG.update(deps.storage, |mut config| -> StdResult<_> { 219 | config.allowed_mint_addresses = updated; 220 | Ok(config) 221 | })?; 222 | Ok(Response::new().add_attribute("method", "remove_whitelist")) 223 | } 224 | 225 | ExecuteMsg::AddDenom { denoms } => { 226 | let config = CONFIG.load(deps.storage)?; 227 | is_contract_manager(config.clone(), info.sender)?; 228 | 229 | let mut updated_denoms = config.denoms; 230 | for new in denoms { 231 | if !updated_denoms.contains(&new) { 232 | updated_denoms.push(new); 233 | } 234 | } 235 | 236 | CONFIG.update(deps.storage, |mut config| -> StdResult<_> { 237 | config.denoms = updated_denoms; 238 | Ok(config) 239 | })?; 240 | 241 | Ok(Response::new().add_attribute("method", "add_denom")) 242 | } 243 | ExecuteMsg::RemoveDenom { denoms } => { 244 | let config = CONFIG.load(deps.storage)?; 245 | is_contract_manager(config.clone(), info.sender)?; 246 | 247 | let mut updated_denoms = config.denoms; 248 | for remove in denoms { 249 | updated_denoms.retain(|a| a != &remove); 250 | } 251 | 252 | CONFIG.update(deps.storage, |mut config| -> StdResult<_> { 253 | config.denoms = updated_denoms; 254 | Ok(config) 255 | })?; 256 | Ok(Response::new().add_attribute("method", "remove_denom")) 257 | } 258 | } 259 | } 260 | 261 | pub fn execute_transfer_admin( 262 | deps: DepsMut, 263 | info: MessageInfo, 264 | denom: String, 265 | new_addr: String, 266 | ) -> Result, ContractError> { 267 | let config = CONFIG.load(deps.storage)?; 268 | is_contract_manager(config.clone(), info.sender)?; 269 | 270 | // it is possible to transfer admin in without adding to contract config. So devs need a way to reclaim admin without adding it to denoms config 271 | let config_denom: Option<&String> = config.denoms.iter().find(|d| d.to_string() == denom); 272 | 273 | if let Some(config_denom) = config_denom { 274 | // remove it from config 275 | let updated_config: Vec = config 276 | .denoms 277 | .iter() 278 | .filter(|d| d.to_string() != *config_denom) 279 | .map(|d| d.to_string()) 280 | .collect(); 281 | 282 | CONFIG.update(deps.storage, |mut config| -> StdResult<_> { 283 | config.denoms = updated_config; 284 | Ok(config) 285 | })?; 286 | } 287 | 288 | let msg = TokenFactoryMsg::ChangeAdmin { 289 | denom: denom.to_string(), 290 | new_admin_address: new_addr.to_string(), 291 | }; 292 | 293 | Ok(Response::new() 294 | .add_attribute("method", "execute_transfer_admin") 295 | .add_attribute("new_admin", new_addr) 296 | .add_message(msg)) 297 | } 298 | 299 | pub fn execute_mint( 300 | deps: DepsMut, 301 | info: MessageInfo, 302 | address: String, 303 | denoms: Vec, 304 | ) -> Result, ContractError> { 305 | let config = CONFIG.load(deps.storage)?; 306 | 307 | is_whitelisted(config, info.sender)?; 308 | 309 | let mint_msgs: Vec = mint_factory_token_messages(&address, &denoms)?; 310 | 311 | Ok(Response::new() 312 | .add_attribute("method", "execute_mint") 313 | .add_attribute("to_address", address) 314 | .add_attribute("denoms", pretty_denoms_output(&denoms)) 315 | .add_messages(mint_msgs)) 316 | } 317 | 318 | pub fn execute_burn( 319 | deps: DepsMut, 320 | env: Env, 321 | info: MessageInfo, 322 | ) -> Result, ContractError> { 323 | // Anyone can burn funds since they have to send them in. 324 | if info.funds.is_empty() { 325 | return Err(ContractError::InvalidFunds {}); 326 | } 327 | 328 | let config = CONFIG.load(deps.storage)?; 329 | 330 | let (factory_denoms, send_back): (Vec, Vec) = info 331 | .funds 332 | .iter() 333 | .cloned() 334 | .partition(|coin| config.denoms.iter().any(|d| *d == coin.denom)); 335 | 336 | let burn_msgs: Vec = factory_denoms 337 | .iter() 338 | .map(|coin| TokenFactoryMsg::BurnTokens { 339 | denom: coin.denom.clone(), 340 | amount: coin.amount, 341 | burn_from_address: env.contract.address.to_string(), 342 | }) 343 | .collect(); 344 | 345 | let bank_return_msg = BankMsg::Send { 346 | to_address: info.sender.to_string(), 347 | amount: send_back, 348 | }; 349 | 350 | Ok(Response::new() 351 | .add_attribute("method", "execute_burn") 352 | .add_message(bank_return_msg) 353 | .add_messages(burn_msgs)) 354 | } 355 | 356 | #[cfg_attr(not(feature = "library"), entry_point)] 357 | pub fn query(deps: Deps, _env: Env, msg: QueryMsg) -> StdResult { 358 | match msg { 359 | QueryMsg::GetConfig {} => { 360 | let config = CONFIG.load(deps.storage)?; 361 | to_binary(&config) 362 | } 363 | QueryMsg::GetBalance { address, denom } => { 364 | let v = BankQuery::Balance { address, denom }; 365 | let res: BalanceResponse = deps.querier.query(&v.into())?; 366 | to_binary(&res.amount) 367 | } 368 | 369 | // Since RPC's do not like to return factory/ denoms. We allow that through this query 370 | QueryMsg::GetAllBalances { address } => { 371 | let v = BankQuery::AllBalances { address }; 372 | 373 | let res: AllBalanceResponse = deps.querier.query(&v.into())?; 374 | 375 | to_binary(&res.amount) 376 | } 377 | } 378 | } 379 | -------------------------------------------------------------------------------- /contracts/tokenfactory_core/src/error.rs: -------------------------------------------------------------------------------- 1 | use cosmwasm_std::StdError; 2 | use thiserror::Error; 3 | 4 | #[derive(Error, Debug, PartialEq)] 5 | pub enum ContractError { 6 | #[error("{0}")] 7 | Std(#[from] StdError), 8 | 9 | #[error("Unauthorized")] 10 | Unauthorized {}, 11 | 12 | #[error("Invalid denom: {denom:?} {message:?}")] 13 | InvalidDenom { denom: String, message: String }, 14 | 15 | #[error("You did not specify any denoms.")] 16 | NoDenomsProvided {}, 17 | 18 | #[error("Invalid funds")] 19 | InvalidFunds {}, 20 | } 21 | -------------------------------------------------------------------------------- /contracts/tokenfactory_core/src/helpers.rs: -------------------------------------------------------------------------------- 1 | use cosmwasm_std::{Addr, Coin, Uint128}; 2 | use token_bindings::{DenomUnit, Metadata, TokenFactoryMsg}; 3 | 4 | use crate::{msg::NewDenom, state::Config, ContractError}; 5 | 6 | pub use juno_tokenfactory_types::msg::ExecuteMsg::Mint; 7 | 8 | pub fn is_whitelisted(state: Config, sender: Addr) -> Result<(), ContractError> { 9 | if !state.allowed_mint_addresses.contains(&sender.to_string()) { 10 | return Err(ContractError::Unauthorized {}); 11 | } 12 | Ok(()) 13 | } 14 | 15 | pub fn is_contract_manager(config: Config, sender: Addr) -> Result<(), ContractError> { 16 | if !config.manager.eq(&sender.to_string()) { 17 | return Err(ContractError::Unauthorized {}); 18 | } 19 | Ok(()) 20 | } 21 | 22 | /// Creates the token messages to mint factory tokens to an address (from this middleware contract) 23 | /// If there are no denoms provided to mint (standard coins), it will return an error 24 | /// 25 | /// You should not use this function unless you are within this contract. It is not for other contract use 26 | /// unless you also use TokenFactoryMsg's, which is the entire point of this contract to not have to do. 27 | pub fn mint_factory_token_messages( 28 | address: &String, 29 | denoms: &Vec, 30 | ) -> Result, ContractError> { 31 | if denoms.is_empty() { 32 | return Err(ContractError::NoDenomsProvided {}); 33 | } 34 | 35 | let msgs: Vec = denoms 36 | .iter() 37 | .filter(|d| denoms.iter().any(|d2| d2.denom == d.denom)) 38 | .map(|d| TokenFactoryMsg::MintTokens { 39 | denom: d.denom.clone(), 40 | amount: d.amount, 41 | mint_to_address: address.to_string(), 42 | }) 43 | .collect(); 44 | 45 | Ok(msgs) 46 | } 47 | 48 | // Makes the output of a vector of denominations much pretty. In the format: 49 | // 1000000:factory/juno1xxx/test, 1000000:factory/juno1xxx/test2 50 | pub fn pretty_denoms_output(denoms: &[Coin]) -> String { 51 | denoms 52 | .iter() 53 | .map(|d| format!("{}:{}", d.amount, d.denom)) 54 | .collect::>() 55 | .join(", ") 56 | } 57 | 58 | pub fn create_denom_msg(subdenom: String, full_denom: String, denom: NewDenom) -> TokenFactoryMsg { 59 | TokenFactoryMsg::CreateDenom { 60 | subdenom, 61 | metadata: Some(Metadata { 62 | name: Some(denom.name), 63 | description: denom.description, 64 | denom_units: vec![ 65 | DenomUnit { 66 | denom: full_denom.clone(), 67 | exponent: 0, 68 | aliases: vec![], 69 | }, 70 | DenomUnit { 71 | denom: denom.symbol.clone(), 72 | exponent: denom.decimals, 73 | aliases: vec![], 74 | }, 75 | ], 76 | base: Some(full_denom), 77 | display: Some(denom.symbol.clone()), 78 | symbol: Some(denom.symbol), 79 | }), 80 | } 81 | } 82 | 83 | pub fn mint_tokens_msg(address: String, denom: String, amount: Uint128) -> TokenFactoryMsg { 84 | TokenFactoryMsg::MintTokens { 85 | denom, 86 | amount, 87 | mint_to_address: address, 88 | } 89 | } 90 | -------------------------------------------------------------------------------- /contracts/tokenfactory_core/src/lib.rs: -------------------------------------------------------------------------------- 1 | pub mod contract; 2 | mod error; 3 | pub mod helpers; 4 | pub mod msg; 5 | pub mod state; 6 | 7 | pub use crate::error::ContractError; 8 | -------------------------------------------------------------------------------- /contracts/tokenfactory_core/src/msg.rs: -------------------------------------------------------------------------------- 1 | use cosmwasm_schema::{cw_serde, QueryResponses}; 2 | 3 | #[cw_serde] 4 | pub struct InstantiateMsg { 5 | // the manager of the contract is the one who can transfer the admin to another address 6 | // Typically this should be a multisig or a DAO (https://daodao.zone/) 7 | // Default is the contract initializer 8 | pub manager: Option, 9 | pub allowed_mint_addresses: Vec, 10 | 11 | // We can manage multiple denoms 12 | pub existing_denoms: Option>, // ex: factory/juno1xxx/test 13 | pub new_denoms: Option>, 14 | } 15 | 16 | #[cw_serde] 17 | pub struct NewDenom { 18 | pub name: String, 19 | pub description: Option, 20 | pub symbol: String, 21 | pub decimals: u32, 22 | pub initial_balances: Option>, 23 | } 24 | 25 | #[cw_serde] 26 | pub struct InitialBalance { 27 | pub address: String, 28 | pub amount: Uint128, 29 | } 30 | 31 | use cosmwasm_std::{Coin, Uint128}; 32 | pub use juno_tokenfactory_types::msg::ExecuteMsg; 33 | 34 | #[cw_serde] 35 | #[derive(QueryResponses)] 36 | pub enum QueryMsg { 37 | #[returns(crate::state::Config)] 38 | GetConfig {}, 39 | 40 | #[returns(Coin)] 41 | GetBalance { address: String, denom: String }, 42 | 43 | #[returns(Vec)] 44 | GetAllBalances { address: String }, 45 | } 46 | -------------------------------------------------------------------------------- /contracts/tokenfactory_core/src/state.rs: -------------------------------------------------------------------------------- 1 | use cosmwasm_schema::cw_serde; 2 | use cw_storage_plus::Item; 3 | 4 | #[cw_serde] 5 | pub struct Config { 6 | pub manager: String, 7 | pub allowed_mint_addresses: Vec, 8 | pub denoms: Vec, 9 | } 10 | 11 | pub const CONFIG: Item = Item::new("config"); 12 | -------------------------------------------------------------------------------- /packages/tokenfactory-types/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "juno-tokenfactory-types" 3 | description = "The execute msg types for Juno's TokenFactory module." 4 | license = "MIT OR Apache-2.0" 5 | version = "0.1.0" 6 | authors = ["Reece Williams "] 7 | edition = "2018" 8 | 9 | [dependencies] 10 | cosmwasm-schema = "1.1" 11 | cosmwasm-std = "1.1" 12 | schemars = "0.8" 13 | serde = { version = "1.0", default-features = false, features = ["derive"] } 14 | token-bindings = "0.11.0" 15 | 16 | [dev-dependencies] -------------------------------------------------------------------------------- /packages/tokenfactory-types/src/lib.rs: -------------------------------------------------------------------------------- 1 | pub mod msg; 2 | -------------------------------------------------------------------------------- /packages/tokenfactory-types/src/msg.rs: -------------------------------------------------------------------------------- 1 | use cosmwasm_schema::cw_serde; 2 | 3 | use cosmwasm_std::Coin; 4 | // use token_bindings::Metadata; 5 | use token_bindings::Metadata; 6 | 7 | #[cw_serde] 8 | pub enum ExecuteMsg { 9 | // == ANYONE == 10 | Burn {}, 11 | 12 | // == WHITELIST == 13 | // Mints actual tokens to an address (only whitelisted addresses can do this) 14 | Mint { 15 | address: String, 16 | denom: Vec, 17 | }, 18 | 19 | // == MANAGER == 20 | BurnFrom { 21 | from: String, 22 | denom: Coin, 23 | }, 24 | 25 | TransferAdmin { 26 | denom: String, 27 | new_address: String, 28 | }, 29 | 30 | ForceTransfer { 31 | from: String, 32 | to: String, 33 | denom: Coin, 34 | }, 35 | 36 | SetMetadata { 37 | denom: String, 38 | metadata: Metadata, 39 | }, 40 | 41 | // Could be a DAO, normal contract, or CW4 42 | // Future: should we specify what name/denom an address can mint? 43 | AddWhitelist { 44 | addresses: Vec, 45 | }, 46 | RemoveWhitelist { 47 | addresses: Vec, 48 | }, 49 | 50 | AddDenom { 51 | denoms: Vec, 52 | }, 53 | RemoveDenom { 54 | denoms: Vec, 55 | }, 56 | } 57 | -------------------------------------------------------------------------------- /rustfmt.toml: -------------------------------------------------------------------------------- 1 | # stable 2 | newline_style = "unix" 3 | hard_tabs = false 4 | tab_spaces = 4 5 | 6 | # unstable... should we require `rustup run nightly cargo fmt` ? 7 | # or just update the style guide when they are stable? 8 | #fn_single_line = true 9 | #format_code_in_doc_comments = true 10 | #overflow_delimited_expr = true 11 | #reorder_impl_items = true 12 | #struct_field_align_threshold = 20 13 | #struct_lit_single_line = true 14 | #report_todo = "Always" 15 | 16 | -------------------------------------------------------------------------------- /scripts/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CosmosContracts/tokenfactory-contracts/d823f37582df317476798a448e1ffe786641f62e/scripts/.gitkeep -------------------------------------------------------------------------------- /scripts/mainnet_upload.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # Uploads the contracts to Mainnet & returns their code_ids 3 | # 4 | # Usage: sh scripts/mainnet_upload.sh 5 | # 6 | 7 | KEY="reece-main" 8 | CHAINID="juno-1" 9 | JUNOD_HOME="$HOME/.juno" 10 | KEYALGO="secp256k1" 11 | KEYRING="os" 12 | 13 | export JUNOD_NODE="https://rpc.juno.strange.love:443" 14 | export JUNOD_COMMAND_ARGS="--gas=auto -y --from $KEY --broadcast-mode block --output json --chain-id=$CHAINID --gas-prices=0.075ujuno --gas-adjustment=1.3 --home "$JUNOD_HOME" --keyring-backend $KEYRING" 15 | 16 | middleware_tx=$(junod tx wasm store artifacts/juno_tokenfactory_core.wasm $JUNOD_COMMAND_ARGS | jq -r .txhash) 17 | middleware_code_id=$(junod q tx $middleware_tx --output=json | jq -r '.logs[0].events[] | select(.type=="store_code")' | jq -r .attributes[1].value) && echo "Middleware code_id: $middleware_code_id" 18 | 19 | migrate_tx=$(junod tx wasm store artifacts/migrate.wasm $JUNOD_COMMAND_ARGS | jq -r .txhash) # && echo "Migrate tx: $migrate_tx" 20 | migrate_code_id=$(junod q tx $migrate_tx --output=json | jq -r '.logs[].events[] | select(.type=="store_code")' | jq -r .attributes[1].value) && echo "Migrate code_id: $migrate_code_id" -------------------------------------------------------------------------------- /scripts/testnet_upload.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # Uploads the contracts & returns their code_ids 3 | # 4 | # Usage: sh scripts/testnet_upload.sh 5 | # 6 | 7 | KEY="validator" 8 | CHAINID="uni-6" 9 | JUNOD_HOME="$HOME/.juno" 10 | KEYALGO="secp256k1" 11 | KEYRING="os" 12 | 13 | export JUNOD_NODE="https://uni-rpc.reece.sh:443" 14 | export JUNOD_COMMAND_ARGS="--gas=auto -y --from $KEY --broadcast-mode=sync --output json --chain-id=$CHAINID --gas-prices=0.003ujunox --gas-adjustment=1.5 --home "$JUNOD_HOME" --keyring-backend $KEYRING" 15 | 16 | middleware_tx=$(junod tx wasm store artifacts/juno_tokenfactory_core.wasm $JUNOD_COMMAND_ARGS | jq -r .txhash) 17 | middleware_code_id=$(junod q tx $middleware_tx --output=json | jq -r '.logs[0].events[] | select(.type=="store_code")' | jq -r .attributes[1].value) && echo "Middleware code_id: $middleware_code_id" 18 | 19 | migrate_tx=$(junod tx wasm store artifacts/migrate.wasm $JUNOD_COMMAND_ARGS | jq -r .txhash) # && echo "Migrate tx: $migrate_tx" 20 | migrate_code_id=$(junod q tx $migrate_tx --output=json | jq -r '.logs[].events[] | select(.type=="store_code")' | jq -r .attributes[1].value) && echo "Migrate code_id: $migrate_code_id" -------------------------------------------------------------------------------- /test/go.mod: -------------------------------------------------------------------------------- 1 | module github.com/CosmosContracts/tokenfactory-contracts 2 | 3 | go 1.19 4 | 5 | replace ( 6 | github.com/ChainSafe/go-schnorrkel => github.com/ChainSafe/go-schnorrkel v0.0.0-20200405005733-88cbf1b4c40d 7 | github.com/ChainSafe/go-schnorrkel/1 => github.com/ChainSafe/go-schnorrkel v1.0.0 8 | 9 | // github.com/CosmosContracts/juno/v17 => github.com/CosmosContracts/juno/v17 v17.0.1-0.20230916185120-6a341ede9264 10 | github.com/btcsuite/btcd => github.com/btcsuite/btcd v0.22.2 //indirect 11 | github.com/gogo/protobuf => github.com/regen-network/protobuf v1.3.3-alpha.regen.1 12 | 13 | github.com/vedhavyas/go-subkey => github.com/strangelove-ventures/go-subkey v1.0.7 14 | 15 | ) 16 | 17 | require ( 18 | github.com/CosmWasm/wasmd v0.41.0 19 | github.com/CosmosContracts/juno/v17 v17.0.0 20 | github.com/cosmos/cosmos-sdk v0.47.5 21 | github.com/docker/docker v24.0.5+incompatible 22 | github.com/strangelove-ventures/interchaintest/v7 v7.0.0-20231025031208-463fdf2292e8 23 | github.com/stretchr/testify v1.8.4 24 | go.uber.org/zap v1.26.0 25 | gotest.tools v2.2.0+incompatible 26 | ) 27 | 28 | require ( 29 | cloud.google.com/go v0.110.4 // indirect 30 | cloud.google.com/go/compute v1.20.1 // indirect 31 | cloud.google.com/go/compute/metadata v0.2.3 // indirect 32 | cloud.google.com/go/iam v1.1.0 // indirect 33 | cloud.google.com/go/storage v1.30.1 // indirect 34 | cosmossdk.io/api v0.3.1 // indirect 35 | cosmossdk.io/core v0.6.1 // indirect 36 | cosmossdk.io/depinject v1.0.0-alpha.4 // indirect 37 | cosmossdk.io/errors v1.0.0 // indirect 38 | cosmossdk.io/log v1.2.1 // indirect 39 | cosmossdk.io/math v1.1.2 // indirect 40 | cosmossdk.io/tools/rosetta v0.2.1 // indirect 41 | filippo.io/edwards25519 v1.0.0 // indirect 42 | github.com/99designs/go-keychain v0.0.0-20191008050251-8e49817e8af4 // indirect 43 | github.com/99designs/keyring v1.2.2 // indirect 44 | github.com/BurntSushi/toml v1.3.2 // indirect 45 | github.com/ChainSafe/go-schnorrkel v1.0.0 // indirect 46 | github.com/ChainSafe/go-schnorrkel/1 v0.0.0-00010101000000-000000000000 // indirect 47 | github.com/ComposableFi/go-subkey/v2 v2.0.0-tm03420 // indirect 48 | github.com/CosmWasm/wasmvm v1.3.0 // indirect 49 | github.com/FactomProject/basen v0.0.0-20150613233007-fe3947df716e // indirect 50 | github.com/FactomProject/btcutilecc v0.0.0-20130527213604-d3a63a5752ec // indirect 51 | github.com/Microsoft/go-winio v0.6.0 // indirect 52 | github.com/StirlingMarketingGroup/go-namecase v1.0.0 // indirect 53 | github.com/armon/go-metrics v0.4.1 // indirect 54 | github.com/avast/retry-go/v4 v4.5.0 // indirect 55 | github.com/aws/aws-sdk-go v1.44.203 // indirect 56 | github.com/beorn7/perks v1.0.1 // indirect 57 | github.com/bgentry/go-netrc v0.0.0-20140422174119-9fd32a8b3d3d // indirect 58 | github.com/bgentry/speakeasy v0.1.1-0.20220910012023-760eaf8b6816 // indirect 59 | github.com/btcsuite/btcd/btcec/v2 v2.3.2 // indirect 60 | github.com/cenkalti/backoff/v4 v4.2.1 // indirect 61 | github.com/cespare/xxhash v1.1.0 // indirect 62 | github.com/cespare/xxhash/v2 v2.2.0 // indirect 63 | github.com/chzyer/readline v1.5.1 // indirect 64 | github.com/cockroachdb/errors v1.11.1 // indirect 65 | github.com/cockroachdb/logtags v0.0.0-20230118201751-21c54148d20b // indirect 66 | github.com/cockroachdb/redact v1.1.5 // indirect 67 | github.com/coinbase/rosetta-sdk-go/types v1.0.0 // indirect 68 | github.com/cometbft/cometbft v0.37.2 // indirect 69 | github.com/cometbft/cometbft-db v0.8.0 // indirect 70 | github.com/confio/ics23/go v0.9.0 // indirect 71 | github.com/cosmos/btcutil v1.0.5 // indirect 72 | github.com/cosmos/cosmos-proto v1.0.0-beta.3 // indirect 73 | github.com/cosmos/go-bip39 v1.0.0 // indirect 74 | github.com/cosmos/gogogateway v1.2.0 // indirect 75 | github.com/cosmos/gogoproto v1.4.10 // indirect 76 | github.com/cosmos/iavl v0.20.0 // indirect 77 | github.com/cosmos/ibc-go/modules/capability v1.0.0-rc1 // indirect 78 | github.com/cosmos/ibc-go/v7 v7.3.0 // indirect 79 | github.com/cosmos/ics23/go v0.10.0 // indirect 80 | github.com/cosmos/ledger-cosmos-go v0.13.0 // indirect 81 | github.com/cosmos/rosetta-sdk-go v0.10.0 // indirect 82 | github.com/creachadair/taskgroup v0.4.2 // indirect 83 | github.com/danieljoos/wincred v1.1.2 // indirect 84 | github.com/davecgh/go-spew v1.1.1 // indirect 85 | github.com/deckarep/golang-set v1.8.0 // indirect 86 | github.com/decred/base58 v1.0.4 // indirect 87 | github.com/decred/dcrd/crypto/blake256 v1.0.1 // indirect 88 | github.com/decred/dcrd/dcrec/secp256k1/v2 v2.0.1 // indirect 89 | github.com/decred/dcrd/dcrec/secp256k1/v4 v4.2.0 // indirect 90 | github.com/desertbit/timer v0.0.0-20180107155436-c41aec40b27f // indirect 91 | github.com/dgraph-io/badger/v2 v2.2007.4 // indirect 92 | github.com/dgraph-io/ristretto v0.1.1 // indirect 93 | github.com/dgryski/go-farm v0.0.0-20200201041132-a6ae2369ad13 // indirect 94 | github.com/docker/distribution v2.8.2+incompatible // indirect 95 | github.com/docker/go-connections v0.4.0 // indirect 96 | github.com/docker/go-units v0.5.0 // indirect 97 | github.com/dustin/go-humanize v1.0.1 // indirect 98 | github.com/dvsekhvalnov/jose2go v1.5.0 // indirect 99 | github.com/ethereum/go-ethereum v1.10.20 // indirect 100 | github.com/felixge/httpsnoop v1.0.2 // indirect 101 | github.com/fsnotify/fsnotify v1.6.0 // indirect 102 | github.com/getsentry/sentry-go v0.23.0 // indirect 103 | github.com/go-kit/kit v0.12.0 // indirect 104 | github.com/go-kit/log v0.2.1 // indirect 105 | github.com/go-logfmt/logfmt v0.6.0 // indirect 106 | github.com/go-stack/stack v1.8.1 // indirect 107 | github.com/godbus/dbus v0.0.0-20190726142602-4481cbc300e2 // indirect 108 | github.com/gogo/googleapis v1.4.1 // indirect 109 | github.com/gogo/protobuf v1.3.3 // indirect 110 | github.com/golang/glog v1.1.0 // indirect 111 | github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect 112 | github.com/golang/mock v1.6.0 // indirect 113 | github.com/golang/protobuf v1.5.3 // indirect 114 | github.com/golang/snappy v0.0.4 // indirect 115 | github.com/google/btree v1.1.2 // indirect 116 | github.com/google/go-cmp v0.5.9 // indirect 117 | github.com/google/orderedcode v0.0.1 // indirect 118 | github.com/google/s2a-go v0.1.4 // indirect 119 | github.com/google/uuid v1.3.0 // indirect 120 | github.com/googleapis/enterprise-certificate-proxy v0.2.3 // indirect 121 | github.com/googleapis/gax-go/v2 v2.11.0 // indirect 122 | github.com/gorilla/handlers v1.5.1 // indirect 123 | github.com/gorilla/mux v1.8.0 // indirect 124 | github.com/gorilla/websocket v1.5.0 // indirect 125 | github.com/grpc-ecosystem/go-grpc-middleware v1.4.0 // indirect 126 | github.com/grpc-ecosystem/grpc-gateway v1.16.0 // indirect 127 | github.com/gsterjov/go-libsecret v0.0.0-20161001094733-a6f4afe4910c // indirect 128 | github.com/gtank/merlin v0.1.1 // indirect 129 | github.com/gtank/ristretto255 v0.1.2 // indirect 130 | github.com/hashicorp/go-cleanhttp v0.5.2 // indirect 131 | github.com/hashicorp/go-getter v1.7.1 // indirect 132 | github.com/hashicorp/go-immutable-radix v1.3.1 // indirect 133 | github.com/hashicorp/go-safetemp v1.0.0 // indirect 134 | github.com/hashicorp/go-version v1.6.0 // indirect 135 | github.com/hashicorp/golang-lru v0.5.5-0.20210104140557-80c98217689d // indirect 136 | github.com/hashicorp/hcl v1.0.0 // indirect 137 | github.com/hdevalence/ed25519consensus v0.1.0 // indirect 138 | github.com/huandu/skiplist v1.2.0 // indirect 139 | github.com/icza/dyno v0.0.0-20220812133438-f0b6f8a18845 // indirect 140 | github.com/improbable-eng/grpc-web v0.15.0 // indirect 141 | github.com/inconshreveable/mousetrap v1.1.0 // indirect 142 | github.com/ipfs/go-cid v0.4.1 // indirect 143 | github.com/jmespath/go-jmespath v0.4.0 // indirect 144 | github.com/jmhodges/levigo v1.0.0 // indirect 145 | github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51 // indirect 146 | github.com/klauspost/compress v1.16.7 // indirect 147 | github.com/klauspost/cpuid/v2 v2.2.4 // indirect 148 | github.com/kr/pretty v0.3.1 // indirect 149 | github.com/kr/text v0.2.0 // indirect 150 | github.com/lib/pq v1.10.7 // indirect 151 | github.com/libp2p/go-buffer-pool v0.1.0 // indirect 152 | github.com/libp2p/go-libp2p v0.27.8 // indirect 153 | github.com/linxGnu/grocksdb v1.8.0 // indirect 154 | github.com/magiconair/properties v1.8.7 // indirect 155 | github.com/manifoldco/promptui v0.9.0 // indirect 156 | github.com/mattn/go-colorable v0.1.13 // indirect 157 | github.com/mattn/go-isatty v0.0.19 // indirect 158 | github.com/matttproud/golang_protobuf_extensions v1.0.4 // indirect 159 | github.com/mimoo/StrobeGo v0.0.0-20220103164710-9a04d6ca976b // indirect 160 | github.com/minio/highwayhash v1.0.2 // indirect 161 | github.com/minio/sha256-simd v1.0.0 // indirect 162 | github.com/misko9/go-substrate-rpc-client/v4 v4.0.0-20230413215336-5bd2aea337ae // indirect 163 | github.com/mitchellh/go-homedir v1.1.0 // indirect 164 | github.com/mitchellh/go-testing-interface v1.14.1 // indirect 165 | github.com/mitchellh/mapstructure v1.5.0 // indirect 166 | github.com/mr-tron/base58 v1.2.0 // indirect 167 | github.com/mtibben/percent v0.2.1 // indirect 168 | github.com/multiformats/go-base32 v0.1.0 // indirect 169 | github.com/multiformats/go-base36 v0.2.0 // indirect 170 | github.com/multiformats/go-multiaddr v0.9.0 // indirect 171 | github.com/multiformats/go-multibase v0.2.0 // indirect 172 | github.com/multiformats/go-multicodec v0.8.1 // indirect 173 | github.com/multiformats/go-multihash v0.2.1 // indirect 174 | github.com/multiformats/go-varint v0.0.7 // indirect 175 | github.com/opencontainers/go-digest v1.0.0 // indirect 176 | github.com/opencontainers/image-spec v1.1.0-rc2 // indirect 177 | github.com/pelletier/go-toml v1.9.5 // indirect 178 | github.com/pelletier/go-toml/v2 v2.0.9 // indirect 179 | github.com/petermattis/goid v0.0.0-20230518223814-80aa455d8761 // indirect 180 | github.com/pierrec/xxHash v0.1.5 // indirect 181 | github.com/pkg/errors v0.9.1 // indirect 182 | github.com/pmezard/go-difflib v1.0.0 // indirect 183 | github.com/prometheus/client_golang v1.17.0 // indirect 184 | github.com/prometheus/client_model v0.4.1-0.20230718164431-9a2bf3000d16 // indirect 185 | github.com/prometheus/common v0.44.0 // indirect 186 | github.com/prometheus/procfs v0.11.1 // indirect 187 | github.com/rakyll/statik v0.1.7 // indirect 188 | github.com/rcrowley/go-metrics v0.0.0-20201227073835-cf1acfcdf475 // indirect 189 | github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec // indirect 190 | github.com/rogpeppe/go-internal v1.11.0 // indirect 191 | github.com/rs/cors v1.8.3 // indirect 192 | github.com/rs/zerolog v1.31.0 // indirect 193 | github.com/sasha-s/go-deadlock v0.3.1 // indirect 194 | github.com/spaolacci/murmur3 v1.1.0 // indirect 195 | github.com/spf13/afero v1.9.5 // indirect 196 | github.com/spf13/cast v1.5.1 // indirect 197 | github.com/spf13/cobra v1.7.0 // indirect 198 | github.com/spf13/jwalterweatherman v1.1.0 // indirect 199 | github.com/spf13/pflag v1.0.5 // indirect 200 | github.com/spf13/viper v1.16.0 // indirect 201 | github.com/subosito/gotenv v1.4.2 // indirect 202 | github.com/syndtr/goleveldb v1.0.1-0.20220721030215-126854af5e6d // indirect 203 | github.com/tendermint/go-amino v0.16.0 // indirect 204 | github.com/tidwall/btree v1.6.0 // indirect 205 | github.com/tyler-smith/go-bip32 v1.0.0 // indirect 206 | github.com/tyler-smith/go-bip39 v1.1.0 // indirect 207 | github.com/ulikunitz/xz v0.5.11 // indirect 208 | github.com/zondax/hid v0.9.1 // indirect 209 | github.com/zondax/ledger-go v0.14.1 // indirect 210 | go.etcd.io/bbolt v1.3.7 // indirect 211 | go.opencensus.io v0.24.0 // indirect 212 | go.uber.org/multierr v1.11.0 // indirect 213 | golang.org/x/crypto v0.12.0 // indirect 214 | golang.org/x/exp v0.0.0-20230711153332-06a737ee72cb // indirect 215 | golang.org/x/mod v0.12.0 // indirect 216 | golang.org/x/net v0.14.0 // indirect 217 | golang.org/x/oauth2 v0.8.0 // indirect 218 | golang.org/x/sync v0.3.0 // indirect 219 | golang.org/x/sys v0.12.0 // indirect 220 | golang.org/x/term v0.11.0 // indirect 221 | golang.org/x/text v0.12.0 // indirect 222 | golang.org/x/tools v0.12.0 // indirect 223 | golang.org/x/xerrors v0.0.0-20220907171357-04be3eba64a2 // indirect 224 | google.golang.org/api v0.126.0 // indirect 225 | google.golang.org/appengine v1.6.7 // indirect 226 | google.golang.org/genproto v0.0.0-20230706204954-ccb25ca9f130 // indirect 227 | google.golang.org/genproto/googleapis/api v0.0.0-20230629202037-9506855d4529 // indirect 228 | google.golang.org/genproto/googleapis/rpc v0.0.0-20230711160842-782d3b101e98 // indirect 229 | google.golang.org/grpc v1.57.0 // indirect 230 | google.golang.org/protobuf v1.31.0 // indirect 231 | gopkg.in/ini.v1 v1.67.0 // indirect 232 | gopkg.in/natefinch/npipe.v2 v2.0.0-20160621034901-c1b8fa8bdcce // indirect 233 | gopkg.in/yaml.v2 v2.4.0 // indirect 234 | gopkg.in/yaml.v3 v3.0.1 // indirect 235 | lukechampine.com/blake3 v1.1.7 // indirect 236 | lukechampine.com/uint128 v1.2.0 // indirect 237 | modernc.org/cc/v3 v3.40.0 // indirect 238 | modernc.org/ccgo/v3 v3.16.13 // indirect 239 | modernc.org/libc v1.24.1 // indirect 240 | modernc.org/mathutil v1.5.0 // indirect 241 | modernc.org/memory v1.6.0 // indirect 242 | modernc.org/opt v0.1.3 // indirect 243 | modernc.org/sqlite v1.25.0 // indirect 244 | modernc.org/strutil v1.1.3 // indirect 245 | modernc.org/token v1.0.1 // indirect 246 | nhooyr.io/websocket v1.8.7 // indirect 247 | pgregory.net/rapid v1.0.0 // indirect 248 | sigs.k8s.io/yaml v1.3.0 // indirect 249 | ) 250 | -------------------------------------------------------------------------------- /test/helpers/cosmwasm.go: -------------------------------------------------------------------------------- 1 | package test 2 | 3 | import ( 4 | "context" 5 | b64 "encoding/base64" 6 | "encoding/json" 7 | "fmt" 8 | "testing" 9 | 10 | "github.com/cosmos/cosmos-sdk/crypto/keyring" 11 | "github.com/strangelove-ventures/interchaintest/v7/chain/cosmos" 12 | "github.com/strangelove-ventures/interchaintest/v7/ibc" 13 | "github.com/strangelove-ventures/interchaintest/v7/testutil" 14 | "github.com/stretchr/testify/require" 15 | ) 16 | 17 | func SetupContract(t *testing.T, ctx context.Context, chain *cosmos.CosmosChain, keyname string, fileLoc string, message string) (codeId, contract string) { 18 | codeId, err := chain.StoreContract(ctx, keyname, fileLoc) 19 | if err != nil { 20 | t.Fatal(err) 21 | } 22 | // require.Equal(t, "1", codeId) 23 | 24 | contractAddr, err := chain.InstantiateContract(ctx, keyname, codeId, message, true) 25 | if err != nil { 26 | t.Fatal(err) 27 | } 28 | // t.Log(contractAddr) 29 | 30 | return codeId, contractAddr 31 | } 32 | 33 | func InstantiateMsgWithGas(t *testing.T, ctx context.Context, chain *cosmos.CosmosChain, user ibc.Wallet, codeId, gas, coinAmt, message string) { 34 | // TODO: ictest does not allow --gas=auto for init yet. So still stuck with this ugh 35 | cmd := []string{"junod", "tx", "wasm", "instantiate", codeId, message, 36 | "--node", chain.GetRPCAddress(), 37 | "--home", chain.HomeDir(), 38 | "--chain-id", chain.Config().ChainID, 39 | "--from", user.KeyName(), 40 | "--gas", gas, 41 | "--label", "contract" + codeId, 42 | "--keyring-dir", chain.HomeDir(), 43 | "--keyring-backend", keyring.BackendTest, 44 | "--no-admin", 45 | "-y", 46 | } 47 | 48 | if len(coinAmt) > 0 { 49 | cmd = append(cmd, "--amount", coinAmt) 50 | } 51 | 52 | stdout, _, err := chain.Exec(ctx, cmd, nil) 53 | require.NoError(t, err) 54 | 55 | debugOutput(t, string(stdout)) 56 | 57 | if err := testutil.WaitForBlocks(ctx, 2, chain); err != nil { 58 | t.Fatal(err) 59 | } 60 | } 61 | 62 | type QueryContractResponse struct { 63 | Contracts []string `json:"contracts"` 64 | } 65 | 66 | func GetContractAddress(ctx context.Context, chain *cosmos.CosmosChain, codeId string) (string, error) { 67 | cmd := []string{"junod", "q", "wasm", "list-contract-by-code", codeId, 68 | "--output", "json", 69 | "--node", chain.GetRPCAddress(), 70 | "--home", chain.HomeDir(), 71 | "--chain-id", chain.Config().ChainID, 72 | } 73 | stdout, _, err := chain.Exec(ctx, cmd, nil) 74 | if err != nil { 75 | return "", fmt.Errorf("error getting contract address: %w", err) 76 | } 77 | 78 | contactsRes := QueryContractResponse{} 79 | if err := json.Unmarshal([]byte(stdout), &contactsRes); err != nil { 80 | return "", fmt.Errorf("error unmarshalling contract address: %w", err) 81 | } 82 | 83 | contractAddress := contactsRes.Contracts[len(contactsRes.Contracts)-1] 84 | return contractAddress, nil 85 | } 86 | 87 | func ExecuteMsgWithAmount(t *testing.T, ctx context.Context, chain *cosmos.CosmosChain, user ibc.Wallet, contractAddr, amount, message string) { 88 | // amount is #utoken 89 | 90 | // There has to be a way to do this in ictest? 91 | cmd := []string{"junod", "tx", "wasm", "execute", contractAddr, message, 92 | "--node", chain.GetRPCAddress(), 93 | "--home", chain.HomeDir(), 94 | "--chain-id", chain.Config().ChainID, 95 | "--from", user.KeyName(), 96 | "--gas", "500000", 97 | "--amount", amount, 98 | "--keyring-dir", chain.HomeDir(), 99 | "--keyring-backend", keyring.BackendTest, 100 | "-y", 101 | } 102 | _, _, err := chain.Exec(ctx, cmd, nil) 103 | require.NoError(t, err) 104 | 105 | // t.Log("msg", cmd) 106 | // t.Log("ExecuteMsgWithAmount", string(stdout)) 107 | 108 | if err := testutil.WaitForBlocks(ctx, 2, chain); err != nil { 109 | t.Fatal(err) 110 | } 111 | } 112 | 113 | func CW20Message(t *testing.T, ctx context.Context, chain *cosmos.CosmosChain, user ibc.Wallet, cw20ContractAddr, actionContractAddr, amount, message string) { 114 | msg := fmt.Sprintf(`{"send":{"contract":"%s","amount":"%s","msg":"%s"}}`, actionContractAddr, amount, b64.StdEncoding.EncodeToString([]byte(message))) 115 | 116 | // not enough gas... used 200k but needs more. So doing manually. 117 | // txHash, _ := chain.ExecuteContract(ctx, user.KeyName(), cw20ContractAddr, msg) 118 | 119 | cmd := []string{"junod", "tx", "wasm", "execute", cw20ContractAddr, msg, 120 | "--node", chain.GetRPCAddress(), 121 | "--home", chain.HomeDir(), 122 | "--chain-id", chain.Config().ChainID, 123 | "--from", user.KeyName(), 124 | "--gas", "500000", 125 | "--keyring-dir", chain.HomeDir(), 126 | "--keyring-backend", keyring.BackendTest, 127 | "-y", 128 | } 129 | _, _, err := chain.Exec(ctx, cmd, nil) 130 | require.NoError(t, err) 131 | 132 | // print stdout 133 | // t.Log("CW20Message", string(stdout)) 134 | 135 | if err := testutil.WaitForBlocks(ctx, 2, chain); err != nil { 136 | t.Fatal(err) 137 | } 138 | } 139 | -------------------------------------------------------------------------------- /test/helpers/tokenfactory.go: -------------------------------------------------------------------------------- 1 | package test 2 | 3 | import ( 4 | "context" 5 | "encoding/json" 6 | "strconv" 7 | "testing" 8 | 9 | "github.com/cosmos/cosmos-sdk/crypto/keyring" 10 | "github.com/strangelove-ventures/interchaintest/v7/chain/cosmos" 11 | "github.com/strangelove-ventures/interchaintest/v7/ibc" 12 | "github.com/strangelove-ventures/interchaintest/v7/testutil" 13 | "github.com/stretchr/testify/require" 14 | 15 | tokenfactorytypes "github.com/CosmosContracts/juno/v17/x/tokenfactory/types" 16 | ) 17 | 18 | type Coin struct { 19 | Denom string `json:"denom"` 20 | Amount string `json:"amount"` 21 | } 22 | 23 | const CHAIN_PREFIX = "juno" 24 | 25 | func debugOutput(t *testing.T, stdout string) { 26 | if true { 27 | t.Log(stdout) 28 | } 29 | } 30 | 31 | func CreateTokenFactoryDenom(t *testing.T, ctx context.Context, chain *cosmos.CosmosChain, user ibc.Wallet, subDenomName string) (fullDenom string) { 32 | // TF gas to create cost 2mil, so we set to 2.5 to be safe 33 | cmd := []string{"junod", "tx", "tokenfactory", "create-denom", subDenomName, 34 | "--node", chain.GetRPCAddress(), 35 | "--home", chain.HomeDir(), 36 | "--chain-id", chain.Config().ChainID, 37 | "--from", user.KeyName(), 38 | "--gas", "2500000", 39 | "--gas-adjustment", "2.0", 40 | "--keyring-dir", chain.HomeDir(), 41 | "--keyring-backend", keyring.BackendTest, 42 | "-y", 43 | } 44 | stdout, _, err := chain.Exec(ctx, cmd, nil) 45 | require.NoError(t, err) 46 | 47 | debugOutput(t, string(stdout)) 48 | 49 | err = testutil.WaitForBlocks(ctx, 2, chain) 50 | require.NoError(t, err) 51 | 52 | return "factory/" + user.FormattedAddress() + "/" + subDenomName 53 | } 54 | 55 | func MintTokenFactoryDenom(t *testing.T, ctx context.Context, chain *cosmos.CosmosChain, admin ibc.Wallet, amount uint64, fullDenom string) { 56 | denom := strconv.FormatUint(amount, 10) + fullDenom 57 | 58 | // mint new tokens to the account 59 | cmd := []string{"junod", "tx", "tokenfactory", "mint", denom, 60 | "--node", chain.GetRPCAddress(), 61 | "--home", chain.HomeDir(), 62 | "--chain-id", chain.Config().ChainID, 63 | "--from", admin.KeyName(), 64 | "--keyring-dir", chain.HomeDir(), 65 | "--keyring-backend", keyring.BackendTest, 66 | "-y", 67 | } 68 | stdout, _, err := chain.Exec(ctx, cmd, nil) 69 | require.NoError(t, err) 70 | 71 | debugOutput(t, string(stdout)) 72 | 73 | err = testutil.WaitForBlocks(ctx, 2, chain) 74 | require.NoError(t, err) 75 | } 76 | 77 | func MintToTokenFactoryDenom(t *testing.T, ctx context.Context, chain *cosmos.CosmosChain, admin ibc.Wallet, toWallet ibc.Wallet, amount uint64, fullDenom string) { 78 | denom := strconv.FormatUint(amount, 10) + fullDenom 79 | 80 | receiver := toWallet.FormattedAddress() 81 | 82 | t.Log("minting", denom, "to", receiver) 83 | 84 | // mint new tokens to the account 85 | cmd := []string{"junod", "tx", "tokenfactory", "mint-to", receiver, denom, 86 | "--node", chain.GetRPCAddress(), 87 | "--home", chain.HomeDir(), 88 | "--chain-id", chain.Config().ChainID, 89 | "--from", admin.KeyName(), 90 | "--keyring-dir", chain.HomeDir(), 91 | "--keyring-backend", keyring.BackendTest, 92 | "-y", 93 | } 94 | stdout, _, err := chain.Exec(ctx, cmd, nil) 95 | require.NoError(t, err) 96 | 97 | debugOutput(t, string(stdout)) 98 | 99 | err = testutil.WaitForBlocks(ctx, 2, chain) 100 | require.NoError(t, err) 101 | } 102 | 103 | func TransferTokenFactoryAdmin(t *testing.T, ctx context.Context, chain *cosmos.CosmosChain, currentAdmin ibc.Wallet, newAdminBech32 string, fullDenom string) { 104 | cmd := []string{"junod", "tx", "tokenfactory", "change-admin", fullDenom, newAdminBech32, 105 | "--node", chain.GetRPCAddress(), 106 | "--home", chain.HomeDir(), 107 | "--chain-id", chain.Config().ChainID, 108 | "--from", currentAdmin.KeyName(), 109 | "--keyring-dir", chain.HomeDir(), 110 | "--keyring-backend", keyring.BackendTest, 111 | "-y", 112 | } 113 | stdout, _, err := chain.Exec(ctx, cmd, nil) 114 | require.NoError(t, err) 115 | 116 | debugOutput(t, string(stdout)) 117 | 118 | err = testutil.WaitForBlocks(ctx, 2, chain) 119 | require.NoError(t, err) 120 | } 121 | 122 | // Getters 123 | func GetTokenFactoryAdmin(t *testing.T, ctx context.Context, chain *cosmos.CosmosChain, fullDenom string) string { 124 | // $BINARY q tokenfactory denom-authority-metadata $FULL_DENOM 125 | 126 | // tokenfactorytypes.QueryDenomAuthorityMetadataRequest{ 127 | // Denom: fullDenom, 128 | // } 129 | cmd := []string{"junod", "query", "tokenfactory", "denom-authority-metadata", fullDenom, 130 | "--node", chain.GetRPCAddress(), 131 | "--chain-id", chain.Config().ChainID, 132 | "--output", "json", 133 | } 134 | stdout, _, err := chain.Exec(ctx, cmd, nil) 135 | require.NoError(t, err) 136 | 137 | debugOutput(t, string(stdout)) 138 | 139 | // results := &tokenfactorytypes.DenomAuthorityMetadata{} 140 | results := &tokenfactorytypes.QueryDenomAuthorityMetadataResponse{} 141 | err = json.Unmarshal(stdout, results) 142 | require.NoError(t, err) 143 | 144 | // t.Log(results) 145 | 146 | err = testutil.WaitForBlocks(ctx, 2, chain) 147 | require.NoError(t, err) 148 | 149 | // tokenfactorytypes.DenomAuthorityMetadata{ 150 | // Admin: ..., 151 | // } 152 | return results.AuthorityMetadata.Admin 153 | } 154 | 155 | func GetTokenFactorySupply(t *testing.T, ctx context.Context, chain *cosmos.CosmosChain, fullDenom string) string { 156 | // $BINARY q bank total --denom $FULL_DENOM 157 | 158 | cmd := []string{"junod", "query", "bank", "total", "--denom", fullDenom, 159 | "--node", chain.GetRPCAddress(), 160 | "--chain-id", chain.Config().ChainID, 161 | "--output", "json", 162 | } 163 | stdout, _, err := chain.Exec(ctx, cmd, nil) 164 | require.NoError(t, err) 165 | 166 | debugOutput(t, string(stdout)) 167 | 168 | // results := &tokenfactorytypes.DenomAuthorityMetadata{} 169 | results := &Coin{} 170 | err = json.Unmarshal(stdout, results) 171 | require.NoError(t, err) 172 | 173 | // t.Log(results) 174 | 175 | err = testutil.WaitForBlocks(ctx, 2, chain) 176 | require.NoError(t, err) 177 | 178 | return results.Amount 179 | } 180 | -------------------------------------------------------------------------------- /test/interchaintest/basic_test.go: -------------------------------------------------------------------------------- 1 | package test 2 | 3 | import ( 4 | "fmt" 5 | "testing" 6 | 7 | "github.com/strangelove-ventures/interchaintest/v7" 8 | "github.com/strangelove-ventures/interchaintest/v7/chain/cosmos" 9 | "gotest.tools/assert" 10 | 11 | helpers "github.com/CosmosContracts/tokenfactory-contracts/helpers" 12 | ) 13 | 14 | // This test ensures the basic contract logic works (bindings mostly & transfers) 15 | // Actual contract logic checks are handled in the TestMigrateContract test 16 | func TestBasicContract(t *testing.T) { 17 | t.Parallel() 18 | 19 | // Create chain factory with Juno 20 | chains := CreateBaseChain(t) 21 | ic, ctx, _, _ := BuildInitialChain(t, chains) 22 | juno := chains[0].(*cosmos.CosmosChain) 23 | 24 | // User Setup 25 | users := interchaintest.GetAndFundTestUsers(t, ctx, "default", int64(100_000_000), juno, juno) 26 | user := users[0] 27 | uaddr := user.FormattedAddress() 28 | user2 := users[1] 29 | uaddr2 := user2.FormattedAddress() 30 | 31 | // Create token-factory denom 32 | tfDenom := helpers.CreateTokenFactoryDenom(t, ctx, juno, user, "testdenom") 33 | denomAdmin := helpers.GetTokenFactoryAdmin(t, ctx, juno, tfDenom) 34 | assert.Equal(t, uaddr, denomAdmin) 35 | 36 | // Setup TokenFactory Core contract (mints on yours/contracts behalf) where uaddr can mint for anyone 37 | tfCoreMsg := fmt.Sprintf(`{"allowed_mint_addresses":["%s"],"existing_denoms":["%s"]}`, uaddr, tfDenom) 38 | tfCoreCodeId, tfCoreContractAddr := helpers.SetupContract(t, ctx, juno, user.KeyName(), TF_CORE_FILE, tfCoreMsg) 39 | 40 | assert.Assert(t, len(tfCoreContractAddr) > 0) 41 | res := GetContractConfig(t, ctx, juno, tfCoreContractAddr) 42 | assert.Assert(t, len(res.Data.AllowedMintAddresses) == 1) 43 | assert.Equal(t, res.Data.Denoms[0], tfDenom) 44 | 45 | // transfer admin to the contract 46 | helpers.TransferTokenFactoryAdmin(t, ctx, juno, user, tfCoreContractAddr, tfDenom) 47 | denomAdmin = helpers.GetTokenFactoryAdmin(t, ctx, juno, tfDenom) 48 | assert.Equal(t, tfCoreContractAddr, denomAdmin) 49 | 50 | // Mint 100 tokens to user through the tfCore contract 51 | msg := fmt.Sprintf(`{"mint":{"address":"%s","denom":[{"denom":"%s","amount":"100"}]}}`, uaddr, tfDenom) 52 | if _, err := juno.ExecuteContract(ctx, user.KeyName(), tfCoreContractAddr, msg); err != nil { 53 | t.Fatal(err) 54 | } 55 | 56 | // BALANCES 57 | AssertBalance(t, ctx, juno, uaddr, tfDenom, 100) 58 | // do the same thing but through the TF contract query 59 | balRes := GetCoreContractUserBalance(t, ctx, juno, tfCoreContractAddr, uaddr, tfDenom) 60 | assert.Equal(t, balRes.Data.Amount, "100") 61 | 62 | // Whitelist 63 | // Try to add user to contract whitelist again. 64 | msg = fmt.Sprintf(`{"add_whitelist":{"addresses":["%s"]}}`, uaddr) 65 | if _, err := juno.ExecuteContract(ctx, user.KeyName(), tfCoreContractAddr, msg); err != nil { 66 | t.Fatal(err) 67 | } 68 | 69 | // still is one 70 | res = GetContractConfig(t, ctx, juno, tfCoreContractAddr) 71 | assert.Assert(t, len(res.Data.AllowedMintAddresses) == 1) 72 | 73 | // add a diff user 74 | msg = fmt.Sprintf(`{"add_whitelist":{"addresses":["%s"]}}`, uaddr2) 75 | if _, err := juno.ExecuteContract(ctx, user.KeyName(), tfCoreContractAddr, msg); err != nil { 76 | t.Fatal(err) 77 | } 78 | 79 | res = GetContractConfig(t, ctx, juno, tfCoreContractAddr) 80 | assert.Assert(t, len(res.Data.AllowedMintAddresses) == 2) 81 | 82 | // remove user2 from whitelist 83 | msg = fmt.Sprintf(`{"remove_whitelist":{"addresses":["%s"]}}`, uaddr2) 84 | if _, err := juno.ExecuteContract(ctx, user.KeyName(), tfCoreContractAddr, msg); err != nil { 85 | t.Fatal(err) 86 | } 87 | 88 | res = GetContractConfig(t, ctx, juno, tfCoreContractAddr) 89 | assert.Assert(t, len(res.Data.AllowedMintAddresses) == 1) 90 | 91 | // force transfer 1 token from user to user2 92 | msg = fmt.Sprintf(`{"force_transfer":{"from":"%s","to":"%s","denom":{"denom":"%s","amount":"3"}}}`, uaddr, uaddr2, tfDenom) 93 | if _, err := juno.ExecuteContract(ctx, user.KeyName(), tfCoreContractAddr, msg); err != nil { 94 | t.Fatal(err) 95 | } 96 | AssertBalance(t, ctx, juno, uaddr2, tfDenom, 3) 97 | 98 | msg = fmt.Sprintf(`{"burn_from":{"from":"%s","denom":{"denom":"%s","amount":"1"}}}`, uaddr2, tfDenom) 99 | if _, err := juno.ExecuteContract(ctx, user.KeyName(), tfCoreContractAddr, msg); err != nil { 100 | t.Fatal(err) 101 | } 102 | AssertBalance(t, ctx, juno, uaddr2, tfDenom, 2) 103 | 104 | // mint a token as user2 to user2 addr 105 | 106 | // transfer admin to uaddr2 from contract & remove from being able to mint 107 | msg = fmt.Sprintf(`{"transfer_admin":{"denom":"%s","new_address":"%s"}}`, tfDenom, uaddr2) 108 | if _, err := juno.ExecuteContract(ctx, user.KeyName(), tfCoreContractAddr, msg); err != nil { 109 | t.Fatal(err) 110 | } 111 | 112 | denomAdmin = helpers.GetTokenFactoryAdmin(t, ctx, juno, tfDenom) 113 | assert.Equal(t, uaddr2, denomAdmin) 114 | 115 | // DENOM WHITELIST 116 | // adds a denom (Only allow factory/ in the future?) 117 | msg = fmt.Sprintf(`{"add_denom":{"denoms":["%s"]}}`, "randomdenom") 118 | if _, err := juno.ExecuteContract(ctx, user.KeyName(), tfCoreContractAddr, msg); err != nil { 119 | t.Fatal(err) 120 | } 121 | 122 | res = GetContractConfig(t, ctx, juno, tfCoreContractAddr) 123 | assert.Assert(t, len(res.Data.Denoms) == 1) 124 | 125 | // Remove denom 126 | msg = fmt.Sprintf(`{"remove_denom":{"denoms":["%s"]}}`, "randomdenom") 127 | if _, err := juno.ExecuteContract(ctx, user.KeyName(), tfCoreContractAddr, msg); err != nil { 128 | t.Fatal(err) 129 | } 130 | 131 | res = GetContractConfig(t, ctx, juno, tfCoreContractAddr) 132 | assert.Assert(t, len(res.Data.Denoms) == 0) 133 | 134 | // Create denom on instantiation 135 | tfMsg := fmt.Sprintf(`{"allowed_mint_addresses":["%s"],"new_denoms":[{"name":"new","description":"desc","symbol":"crt","decimals":6,"initial_balances":[{"address":"%s","amount":"420"}]}]}`, uaddr, uaddr) 136 | 137 | // TODO: When ictest allows for gas=auto on init 138 | // _, tfCoreAddr := helpers.SetupContract(t, ctx, juno, user.KeyName(), TF_CORE_FILE, tfMsg) 139 | helpers.InstantiateMsgWithGas(t, ctx, juno, user, tfCoreCodeId, "5000000", "", tfMsg) 140 | tfCoreAddr, err := helpers.GetContractAddress(ctx, juno, tfCoreCodeId) 141 | if err != nil { 142 | t.Fatal(err) 143 | } 144 | 145 | t.Log("tfCoreCreateAddr", tfCoreAddr) 146 | 147 | tfCreatedDenom := fmt.Sprintf(`factory/%s/crt`, tfCoreAddr) 148 | 149 | res = GetContractConfig(t, ctx, juno, tfCoreAddr) 150 | assert.Equal(t, len(res.Data.Denoms), 1) 151 | assert.Equal(t, res.Data.Denoms[0], tfCreatedDenom) 152 | 153 | // Validate admin. 154 | createdDenomAdmin := helpers.GetTokenFactoryAdmin(t, ctx, juno, tfCreatedDenom) 155 | assert.Equal(t, tfCoreAddr, createdDenomAdmin) 156 | 157 | // Validate initial balances. 158 | AssertBalance(t, ctx, juno, uaddr, tfCreatedDenom, 420) 159 | // do the same thing but through the TF contract query 160 | createdBalRes := GetCoreContractUserBalance(t, ctx, juno, tfCoreAddr, uaddr, tfCreatedDenom) 161 | assert.Equal(t, createdBalRes.Data.Amount, "420") 162 | // Validate supply. 163 | createdSupply := helpers.GetTokenFactorySupply(t, ctx, juno, tfCreatedDenom) 164 | assert.Equal(t, createdSupply, "420") 165 | 166 | // !important: debugging 167 | // t.Log("GetHostRPCAddress", juno.GetHostRPCAddress()) 168 | // testutil.WaitForBlocks(ctx, 20_000, juno) 169 | 170 | // Final Cleanup 171 | t.Cleanup(func() { 172 | _ = ic.Close() 173 | }) 174 | } 175 | -------------------------------------------------------------------------------- /test/interchaintest/cw20_conversion_test.go: -------------------------------------------------------------------------------- 1 | package test 2 | 3 | import ( 4 | "fmt" 5 | "testing" 6 | 7 | "github.com/strangelove-ventures/interchaintest/v7" 8 | "github.com/strangelove-ventures/interchaintest/v7/chain/cosmos" 9 | "gotest.tools/assert" 10 | 11 | helpers "github.com/CosmosContracts/tokenfactory-contracts/helpers" 12 | ) 13 | 14 | func TestCw20ConversionMigrateContract(t *testing.T) { 15 | t.Parallel() 16 | 17 | // Create chain factory with Juno 18 | chains := CreateBaseChain(t) 19 | ic, ctx, _, _ := BuildInitialChain(t, chains) 20 | juno := chains[0].(*cosmos.CosmosChain) 21 | 22 | // User Setup 23 | users := interchaintest.GetAndFundTestUsers(t, ctx, "default", int64(10_000_000), juno, juno) 24 | user := users[0] 25 | uaddr := user.FormattedAddress() 26 | // user2 := users[1] 27 | // uaddr2 := user2.Bech32Address(CHAIN_PREFIX) 28 | 29 | // Create token-factory denom 30 | tfDenom := helpers.CreateTokenFactoryDenom(t, ctx, juno, user, "testdenom") 31 | denomAdmin := helpers.GetTokenFactoryAdmin(t, ctx, juno, tfDenom) 32 | assert.Equal(t, uaddr, denomAdmin) 33 | 34 | // cw20 (We give outselfs 100 to test in the future) 35 | cw20Msg := fmt.Sprintf(`{"name":"test","symbol":"aaaa","decimals":6,"initial_balances":[{"address":"%s","amount":"100"}]}`, uaddr) 36 | _, cw20ContractAddr := helpers.SetupContract(t, ctx, juno, user.KeyName(), "../../base_artifacts/cw20_base.wasm", cw20Msg) 37 | 38 | // Tokenfactory Core minter 39 | tfCoreMsg := fmt.Sprintf(`{"allowed_mint_addresses":[],"existing_denoms":["%s"]}`, tfDenom) 40 | _, tfCoreContractAddr := helpers.SetupContract(t, ctx, juno, user.KeyName(), TF_CORE_FILE, tfCoreMsg) 41 | 42 | // transfer admin to the contract 43 | helpers.TransferTokenFactoryAdmin(t, ctx, juno, user, tfCoreContractAddr, tfDenom) 44 | denomAdmin = helpers.GetTokenFactoryAdmin(t, ctx, juno, tfDenom) 45 | assert.Equal(t, tfCoreContractAddr, denomAdmin) 46 | 47 | // conversion migrate contract (1 CW20 -> contract -> burn CW20 and mint 1 tf denom) 48 | migrateCW20Msg := fmt.Sprintf(`{"cw20_token_address":"%s","contract_minter_address":"%s","tf_denom":"%s"}`, cw20ContractAddr, tfCoreContractAddr, tfDenom) 49 | _, cw20MigrateContractAddr := helpers.SetupContract(t, ctx, juno, user.KeyName(), MIGRATE_FILE, migrateCW20Msg) 50 | 51 | // Allow the Migration contract to mint through the Tokenfactory Core contract 52 | msg := fmt.Sprintf(`{"add_whitelist":{"addresses":["%s"]}}`, cw20MigrateContractAddr) 53 | if _, err := juno.ExecuteContract(ctx, user.KeyName(), tfCoreContractAddr, msg); err != nil { 54 | t.Fatal(err) 55 | } 56 | 57 | // Ensure the contract config data is set correctly. 58 | res := GetContractConfig(t, ctx, juno, tfCoreContractAddr) 59 | assert.Equal(t, res.Data.AllowedMintAddresses[0], cw20MigrateContractAddr) 60 | assert.Equal(t, res.Data.Denoms[0], tfDenom) 61 | 62 | // actual CW20 testing on the contract 63 | // ensure user has 0 tf denom balance 64 | AssertBalance(t, ctx, juno, uaddr, tfDenom, 0) 65 | 66 | // send the message through CW20 -> migrate conversion contract. 67 | // msg = fmt.Sprintf(`{"send":{"contract":"%s","amount":"%s","msg":"%s"}}`, cw20MigrateContractAddr, "5", b64.StdEncoding.EncodeToString([]byte(`{"receive":{}}`))) 68 | // txHash, _ := juno.ExecuteContract(ctx, user.KeyName() cw20ContractAddr, msg) 69 | helpers.CW20Message(t, ctx, juno, user, cw20ContractAddr, cw20MigrateContractAddr, "5", `{"receive":{}}`) 70 | 71 | // we should now have 5 balance of the tf denom 72 | AssertBalance(t, ctx, juno, uaddr, tfDenom, 5) 73 | // the cw20 migrate contract should still have 0 balance of this denom (to ensure it does not double mint) 74 | AssertBalance(t, ctx, juno, cw20MigrateContractAddr, tfDenom, 0) 75 | 76 | // !important: debugging 77 | // t.Log("GetHostRPCAddress", juno.GetHostRPCAddress()) 78 | // testutil.WaitForBlocks(ctx, 20_000, juno) 79 | 80 | // Final Cleanup 81 | t.Cleanup(func() { 82 | _ = ic.Close() 83 | }) 84 | } 85 | -------------------------------------------------------------------------------- /test/interchaintest/native_conversion_test.go: -------------------------------------------------------------------------------- 1 | package test 2 | 3 | import ( 4 | "fmt" 5 | "testing" 6 | 7 | "github.com/strangelove-ventures/interchaintest/v7" 8 | "github.com/strangelove-ventures/interchaintest/v7/chain/cosmos" 9 | "gotest.tools/assert" 10 | 11 | helpers "github.com/CosmosContracts/tokenfactory-contracts/helpers" 12 | ) 13 | 14 | func TestNativeConversionMigrateContract(t *testing.T) { 15 | t.Parallel() 16 | 17 | // Create chain factory with Juno 18 | chains := CreateBaseChain(t) 19 | ic, ctx, _, _ := BuildInitialChain(t, chains) 20 | 21 | // Chains 22 | juno := chains[0].(*cosmos.CosmosChain) 23 | nativeDenom := juno.Config().Denom 24 | 25 | // User Setup 26 | users := interchaintest.GetAndFundTestUsers(t, ctx, "default", int64(10_000_000), juno, juno) 27 | user := users[0] 28 | uaddr := user.FormattedAddress() 29 | t.Log(uaddr) 30 | 31 | user2 := users[1] 32 | uaddr2 := user2.FormattedAddress() 33 | 34 | // ensure user has some ujuno which is not 0 35 | AssertBalance(t, ctx, juno, uaddr, nativeDenom, 10_000_000) 36 | AssertBalance(t, ctx, juno, uaddr2, nativeDenom, 10_000_000) 37 | 38 | // Create token-factory denom 39 | tfDenom := helpers.CreateTokenFactoryDenom(t, ctx, juno, user, "testdenom") 40 | assert.Equal(t, uaddr, helpers.GetTokenFactoryAdmin(t, ctx, juno, tfDenom)) 41 | 42 | // Tokenfactory Core minter 43 | tfCoreMsg := fmt.Sprintf(`{"allowed_mint_addresses":[],"existing_denoms":["%s"]}`, tfDenom) 44 | _, tfCoreContractAddr := helpers.SetupContract(t, ctx, juno, user.KeyName(), TF_CORE_FILE, tfCoreMsg) 45 | 46 | // transfer admin to the contract 47 | helpers.TransferTokenFactoryAdmin(t, ctx, juno, user, tfCoreContractAddr, tfDenom) 48 | assert.Equal(t, tfCoreContractAddr, helpers.GetTokenFactoryAdmin(t, ctx, juno, tfDenom)) 49 | 50 | // conversion migrate contract (1 native -> 1 tf denom) 51 | migrateNativeMsg := fmt.Sprintf(`{"burn_denom":"%s","contract_minter_address":"%s","tf_denom":"%s"}`, nativeDenom, tfCoreContractAddr, tfDenom) 52 | _, naitveMigrateContractAddr := helpers.SetupContract(t, ctx, juno, user.KeyName(), MIGRATE_FILE, migrateNativeMsg) 53 | 54 | // Allow the Migration contract to mint through the Tokenfactory Core contract 55 | msg := fmt.Sprintf(`{"add_whitelist":{"addresses":["%s"]}}`, naitveMigrateContractAddr) 56 | if _, err := juno.ExecuteContract(ctx, user.KeyName(), tfCoreContractAddr, msg); err != nil { 57 | t.Fatal(err) 58 | } 59 | 60 | // Ensure the contract config data is set correctly. 61 | res := GetContractConfig(t, ctx, juno, tfCoreContractAddr) 62 | assert.Equal(t, res.Data.AllowedMintAddresses[0], naitveMigrateContractAddr) 63 | assert.Equal(t, res.Data.Denoms[0], tfDenom) 64 | 65 | // ensure user has 0 tf denom balance 66 | AssertBalance(t, ctx, juno, uaddr, tfDenom, 0) 67 | 68 | // Execute with an amount 69 | helpers.ExecuteMsgWithAmount(t, ctx, juno, user2, naitveMigrateContractAddr, fmt.Sprintf("7%s", nativeDenom), `{"convert":{}}`) 70 | 71 | // Ensure we got the correct amount of tokens in exchange for the native token. (no gas prices) 72 | AssertBalance(t, ctx, juno, uaddr2, tfDenom, 7) 73 | AssertBalance(t, ctx, juno, uaddr2, nativeDenom, 9_999_993) 74 | 75 | // the migrate contract should still have 0 balance of this denom (to ensure it does not double mint) 76 | AssertBalance(t, ctx, juno, naitveMigrateContractAddr, tfDenom, 0) 77 | 78 | // // !important: debugging 79 | // t.Log("GetHostRPCAddress", juno.GetHostRPCAddress()) 80 | // testutil.WaitForBlocks(ctx, 20_000, juno) 81 | 82 | // Final Cleanup 83 | t.Cleanup(func() { 84 | _ = ic.Close() 85 | }) 86 | } 87 | -------------------------------------------------------------------------------- /test/interchaintest/query_helpers.go: -------------------------------------------------------------------------------- 1 | package test 2 | 3 | import ( 4 | "context" 5 | "testing" 6 | 7 | "github.com/strangelove-ventures/interchaintest/v7/chain/cosmos" 8 | "github.com/stretchr/testify/require" 9 | "gotest.tools/assert" 10 | ) 11 | 12 | func AssertBalance(t *testing.T, ctx context.Context, chain *cosmos.CosmosChain, address, denom string, amount int64) { 13 | if bal, err := chain.GetBalance(ctx, address, denom); err != nil { 14 | t.Fatal(err) 15 | } else { 16 | t.Log(address, "balance:", bal, denom) 17 | assert.Equal(t, bal.Int64(), amount) 18 | } 19 | } 20 | 21 | func GetContractConfig(t *testing.T, ctx context.Context, chain *cosmos.CosmosChain, contract string) GetConfigResponse { 22 | // tokenfactory_core/src/state.rs 23 | var cRes GetConfigResponse 24 | err := chain.QueryContract(ctx, contract, QueryMsg{GetConfig: &struct{}{}}, &cRes) 25 | require.NoError(t, err) 26 | t.Log("GetContractConfig", cRes.Data) 27 | return cRes 28 | } 29 | 30 | // TokenFactory Core contract Queries 31 | func GetCoreContractUserBalance(t *testing.T, ctx context.Context, chain *cosmos.CosmosChain, contract, uaddr, tfDenom string) GetBalanceResponse { 32 | var bRes GetBalanceResponse 33 | err := chain.QueryContract(ctx, contract, QueryMsg{GetBalance: &GetBalance{Address: uaddr, Denom: tfDenom}}, &bRes) 34 | require.NoError(t, err) 35 | return bRes 36 | } 37 | 38 | func GetCoreContractUserBalanceAll(t *testing.T, ctx context.Context, chain *cosmos.CosmosChain, contract, uaddr string) GetAllBalancesResponse { 39 | var bRes GetAllBalancesResponse 40 | err := chain.QueryContract(ctx, contract, QueryMsg{GetAllBalances: &GetAllBalances{Address: uaddr}}, &bRes) 41 | require.NoError(t, err) 42 | return bRes 43 | } 44 | -------------------------------------------------------------------------------- /test/interchaintest/setup.go: -------------------------------------------------------------------------------- 1 | package test 2 | 3 | import ( 4 | "context" 5 | "testing" 6 | 7 | // Juno types 8 | wasmtypes "github.com/CosmWasm/wasmd/x/wasm/types" 9 | feesharetypes "github.com/CosmosContracts/juno/v17/x/feeshare/types" 10 | tokenfactorytypes "github.com/CosmosContracts/juno/v17/x/tokenfactory/types" 11 | 12 | testutil "github.com/cosmos/cosmos-sdk/types/module/testutil" 13 | 14 | "github.com/docker/docker/client" 15 | "github.com/strangelove-ventures/interchaintest/v7" 16 | "github.com/strangelove-ventures/interchaintest/v7/chain/cosmos" 17 | "github.com/strangelove-ventures/interchaintest/v7/ibc" 18 | "github.com/strangelove-ventures/interchaintest/v7/testreporter" 19 | "github.com/stretchr/testify/require" 20 | "go.uber.org/zap/zaptest" 21 | ) 22 | 23 | const ( 24 | TF_CORE_FILE = "../../artifacts/juno_tokenfactory_core.wasm" 25 | MIGRATE_FILE = "../../artifacts/migrate.wasm" 26 | ) 27 | 28 | var ( 29 | VotingPeriod = "15s" 30 | MaxDepositPeriod = "10s" 31 | Denom = "ujuno" 32 | 33 | JunoMainRepo = "ghcr.io/cosmoscontracts/juno" 34 | 35 | IBCRelayerImage = "ghcr.io/cosmos/relayer" 36 | IBCRelayerVersion = "main" 37 | 38 | JunoVersion = "v17.0.0" 39 | 40 | // SDK v47 Genesis 41 | defaultGenesisKV = []cosmos.GenesisKV{ 42 | { 43 | Key: "app_state.gov.params.voting_period", 44 | Value: VotingPeriod, 45 | }, 46 | { 47 | Key: "app_state.gov.params.max_deposit_period", 48 | Value: MaxDepositPeriod, 49 | }, 50 | { 51 | Key: "app_state.gov.params.min_deposit.0.denom", 52 | Value: Denom, 53 | }, 54 | // mainnet = 2mil gas, we just require 1 gas for testing 55 | { 56 | Key: "app_state.tokenfactory.params.denom_creation_gas_consume", 57 | Value: 1, 58 | }, 59 | { 60 | Key: "app_state.tokenfactory.params.denom_creation_fee", 61 | Value: nil, 62 | }, 63 | } 64 | ) 65 | 66 | func junoEncoding() *testutil.TestEncodingConfig { 67 | cfg := cosmos.DefaultEncoding() 68 | 69 | // register custom juno types 70 | feesharetypes.RegisterInterfaces(cfg.InterfaceRegistry) 71 | wasmtypes.RegisterInterfaces(cfg.InterfaceRegistry) 72 | tokenfactorytypes.RegisterInterfaces(cfg.InterfaceRegistry) 73 | 74 | return &cfg 75 | } 76 | 77 | // Basic chain setup for a Juno chain. No relaying 78 | func CreateBaseChain(t *testing.T) []ibc.Chain { 79 | // Create chain factory with Juno 80 | numVals := 1 81 | numFullNodes := 0 82 | 83 | cf := interchaintest.NewBuiltinChainFactory(zaptest.NewLogger(t), []*interchaintest.ChainSpec{ 84 | { 85 | Name: "juno", 86 | Version: JunoVersion, 87 | ChainName: "juno1", 88 | ChainConfig: ibc.ChainConfig{ 89 | GasPrices: "0ujuno", 90 | GasAdjustment: 5.0, 91 | EncodingConfig: junoEncoding(), 92 | ModifyGenesis: cosmos.ModifyGenesis(defaultGenesisKV), 93 | }, 94 | NumValidators: &numVals, 95 | NumFullNodes: &numFullNodes, 96 | }, 97 | }) 98 | 99 | // Get chains from the chain factory 100 | chains, err := cf.Chains(t.Name()) 101 | require.NoError(t, err) 102 | 103 | // juno := chains[0].(*cosmos.CosmosChain) 104 | return chains 105 | } 106 | 107 | func BuildInitialChain(t *testing.T, chains []ibc.Chain) (*interchaintest.Interchain, context.Context, *client.Client, string) { 108 | // Create a new Interchain object which describes the chains, relayers, and IBC connections we want to use 109 | ic := interchaintest.NewInterchain() 110 | 111 | for _, chain := range chains { 112 | ic.AddChain(chain) 113 | } 114 | 115 | rep := testreporter.NewNopReporter() 116 | eRep := rep.RelayerExecReporter(t) 117 | 118 | ctx := context.Background() 119 | client, network := interchaintest.DockerSetup(t) 120 | 121 | err := ic.Build(ctx, eRep, interchaintest.InterchainBuildOptions{ 122 | TestName: t.Name(), 123 | Client: client, 124 | NetworkID: network, 125 | SkipPathCreation: true, 126 | // This can be used to write to the block database which will index all block data e.g. txs, msgs, events, etc. 127 | // BlockDatabaseFile: interchaintest.DefaultBlockDatabaseFilepath(), 128 | }) 129 | require.NoError(t, err) 130 | 131 | return ic, ctx, client, network 132 | } 133 | -------------------------------------------------------------------------------- /test/interchaintest/types.go: -------------------------------------------------------------------------------- 1 | package test 2 | 3 | // TODO: Auto generate in the future from Rust types -> Go types? 4 | // Execute types are not needed here. We just use strings. Could add though in the future and to_string it 5 | 6 | // #[returns(Coin)] 7 | // GetBalance { address: String, denom: String }, 8 | 9 | // EntryPoint 10 | type QueryMsg struct { 11 | // GetEntries *GetEntries `json:"get_entries,omitempty"` 12 | GetConfig *struct{} `json:"get_config,omitempty"` 13 | GetBalance *GetBalance `json:"get_balance,omitempty"` 14 | GetAllBalances *GetAllBalances `json:"get_all_balances,omitempty"` 15 | } 16 | 17 | // entry helpers 18 | type GetBalance struct { 19 | Address string `json:"address"` 20 | Denom string `json:"denom"` 21 | } 22 | 23 | type GetAllBalances struct { 24 | Address string `json:"address"` 25 | } 26 | 27 | // Response Types (json is always 'data' from the chain return value) 28 | type GetConfigResponse struct { 29 | Data *ConfigTfCore `json:"data"` 30 | } 31 | 32 | type GetBalanceResponse struct { 33 | Data *Coin `json:"data"` 34 | } 35 | 36 | type GetAllBalancesResponse struct { 37 | Data []Coin `json:"data"` 38 | } 39 | 40 | // type WhitelistResponse struct { 41 | // Data []string `json:"data"` 42 | // } 43 | 44 | // // Middleware 45 | // type GetEntries struct { 46 | // Address string `json:"address"` 47 | // } 48 | 49 | // Base Data Types 50 | type ConfigTfCore struct { 51 | Manager string `json:"manager"` 52 | AllowedMintAddresses []string `json:"allowed_mint_addresses"` 53 | Denoms []string `json:"denoms"` 54 | } 55 | 56 | type Coin struct { 57 | Denom string `json:"denom"` 58 | Amount string `json:"amount"` 59 | } 60 | --------------------------------------------------------------------------------