diff --git a/.circleci/config.yml b/.circleci/config.yml deleted file mode 100644 index 72417cb..0000000 --- a/.circleci/config.yml +++ /dev/null @@ -1,174 +0,0 @@ -version: 2 -workflows: - version: 2 - test: - jobs: - - contract_infinity_swap - - lint - - wasm-build - deploy: - jobs: - - build_and_upload_contracts: - filters: - tags: - only: /^v[0-9]+\.[0-9]+\.[0-9]+.*/ - branches: - ignore: /.*/ - -jobs: - contract_infinity_swap: - docker: - - image: rust:1.68.2 - working_directory: ~/project/contracts/infinity-swap - steps: - - checkout: - path: ~/project - - run: - name: Version information - command: rustc --version; cargo --version; rustup --version - - restore_cache: - keys: - - cargocache-infinity-swap-rust:1.68.2-{{ checksum "~/project/Cargo.lock" }} - - run: - name: Unit Tests - environment: - RUST_BACKTRACE: 1 - command: cargo unit-test - - run: - name: Build and run schema generator - command: cargo schema --locked - - run: - name: Ensure checked-in schemas are up-to-date - command: | - CHANGES_IN_REPO=$(git status --porcelain) - if [[ -n "$CHANGES_IN_REPO" ]]; then - echo "Repository is dirty. Showing 'git status' and 'git --no-pager diff' for debugging now:" - git status && git --no-pager diff - exit 1 - fi - - save_cache: - paths: - - /usr/local/cargo/registry - - target - key: cargocache-infinity-swap-rust:1.68.2-{{ checksum "~/project/Cargo.lock" }} - - lint: - docker: - - image: rust:1.68.2 - steps: - - checkout - - run: - name: Version information - command: rustc --version; cargo --version; rustup --version; rustup target list --installed - - restore_cache: - keys: - - cargocache-v2-lint-rust:1.68.2-{{ checksum "Cargo.lock" }} - - run: - name: Add rustfmt component - command: rustup component add rustfmt - - run: - name: Add clippy component - command: rustup component add clippy - - run: - name: Check formatting of workspace - command: cargo fmt -- --check - - run: - name: Clippy linting on workspace - command: cargo clippy --all-targets -- -D warnings - - save_cache: - paths: - - /usr/local/cargo/registry - - target/debug/.fingerprint - - target/debug/build - - target/debug/deps - key: cargocache-v2-lint-rust:1.68.2-{{ checksum "Cargo.lock" }} - - # This runs one time on the top level to ensure all contracts compile properly into wasm. - # We don't run the wasm build per contract build, and then reuse a lot of the same dependencies, so this speeds up CI time - # for all the other tests. - # We also sanity-check the resultant wasm files. - wasm-build: - docker: - - image: rust:1.68.2 - steps: - - checkout: - path: ~/project - - run: - name: Version information - command: rustc --version; cargo --version; rustup --version - - restore_cache: - keys: - - cargocache-wasm-rust:1.68.2-{{ checksum "~/project/Cargo.lock" }} - - run: - name: Add wasm32 target - command: rustup target add wasm32-unknown-unknown - - run: - name: Build Wasm Release - command: | - for C in ./contracts/*/ - do - echo "Compiling `basename $C`..." - (cd $C && cargo build --release --target wasm32-unknown-unknown --locked) - done - - run: - name: Install check_contract - # Uses --debug for compilation speed - command: cargo install --debug --version 1.0.0 --features iterator --example check_contract -- cosmwasm-vm - - save_cache: - paths: - - /usr/local/cargo/registry - - target - key: cargocache-wasm-rust:1.68.2-{{ checksum "~/project/Cargo.lock" }} - - run: - name: Check wasm contracts - command: | - for W in ./target/wasm32-unknown-unknown/release/*.wasm - do - echo -n "Checking `basename $W`... " - check_contract --supported-features iterator,staking,stargate,stargaze $W - done - - # This job roughly follows the instructions from https://circleci.com/blog/publishing-to-github-releases-via-circleci/ - build_and_upload_contracts: - docker: - # Image from https://github.com/cibuilds/github, based on alpine - - image: cibuilds/github:0.13 - steps: - - run: - name: Install Docker client - command: apk add docker-cli - - setup_remote_docker - - checkout - - run: - # We cannot mount local folders, see https://circleci.com/docs/2.0/building-docker-images/#mounting-folders - name: Prepare volume with source code - command: | - # create a dummy container which will hold a volume with config - docker create -v /code --name with_code alpine /bin/true - # copy a config file into this volume - docker cp Cargo.toml with_code:/code - docker cp Cargo.lock with_code:/code - # copy code into this volume - docker cp ./contracts with_code:/code - - run: - name: Build development contracts - command: | - docker run --volumes-from with_code cosmwasm/workspace-optimizer:0.12.11 - docker cp with_code:/code/artifacts ./artifacts - - run: - name: Show data - command: | - ls -l artifacts - cat artifacts/checksums.txt - - run: - name: Publish artifacts on GitHub - command: | - TAG="$CIRCLE_TAG" - TITLE="$TAG" - BODY="Attached there are some build artifacts generated at this tag. Those are for development purposes only! Please use crates.io to find the packages of this release." - ghr -t "$GITHUB_TOKEN" \ - -u "$CIRCLE_PROJECT_USERNAME" -r "$CIRCLE_PROJECT_REPONAME" \ - -c "$CIRCLE_SHA1" \ - -n "$TITLE" -b "$BODY" \ - -delete \ - "$TAG" ./artifacts/ diff --git a/.github/workflows/basic.yml b/.github/workflows/basic.yml new file mode 100644 index 0000000..97c6190 --- /dev/null +++ b/.github/workflows/basic.yml @@ -0,0 +1,99 @@ +name: Basic + +on: + pull_request: + +jobs: + compile: + name: Compile + runs-on: ubuntu-latest + steps: + - name: Checkout sources + uses: actions/checkout@v2 + + - name: Install stable toolchain + uses: actions-rs/toolchain@v1 + with: + profile: minimal + toolchain: 1.68.2 + target: wasm32-unknown-unknown + override: true + + - name: Compile WASM contract + uses: actions-rs/cargo@v1 + with: + command: wasm + args: --locked + + - name: Unit Tests + run: cargo unit-test --locked + + - name: Install check_contract + run: cargo install --debug --version 1.0.0 --features iterator --example check_contract -- cosmwasm-vm + + - name: Check wasm contracts + run: | + for W in ./target/wasm32-unknown-unknown/release/*.wasm + do + echo -n "Checking `$W`... " + check_contract --supported-features iterator,staking,stargate,stargaze $W + done + + lint: + name: Lint + runs-on: ubuntu-latest + steps: + - name: Checkout sources + uses: actions/checkout@v2 + + - name: Install stable toolchain + uses: actions-rs/toolchain@v1 + with: + profile: minimal + toolchain: 1.68.2 + override: true + components: rustfmt, clippy + + - uses: extractions/setup-just@v1 + + - name: Run cargo doc + uses: actions-rs/cargo@v1 + with: + command: doc + args: -F library --no-deps + + - name: Run cargo clippy + run: just lint + + - name: Generate Schema + run: just schema + + - name: Schema Changes + # fails if any changes not committed + run: | + CHANGES_IN_REPO=$(git status --porcelain) + if [[ -n "$CHANGES_IN_REPO" ]]; then + echo "Repository is dirty. Showing 'git status' and 'git --no-pager diff' for debugging now:" + git status && git --no-pager diff + exit 1 + fi + + coverage: + name: Coverage + runs-on: ubuntu-latest + container: + image: xd009642/tarpaulin:0.24.0 + options: --security-opt seccomp=unconfined + steps: + - name: Checkout repository + uses: actions/checkout@v2 + + - name: Generate code coverage + run: | + cargo tarpaulin --verbose --workspace --timeout 120 --out Xml + + - name: Upload to codecov.io + uses: codecov/codecov-action@v2 + with: + token: ${{secrets.CODECOV_TOKEN}} + fail_ci_if_error: true diff --git a/.github/workflows/coverage.yml b/.github/workflows/coverage.yml deleted file mode 100644 index c45f434..0000000 --- a/.github/workflows/coverage.yml +++ /dev/null @@ -1,25 +0,0 @@ -name: coverage - -on: - pull_request: - push: -jobs: - test: - name: coverage - runs-on: ubuntu-latest - container: - image: xd009642/tarpaulin:0.24.0 - options: --security-opt seccomp=unconfined - steps: - - name: Checkout repository - uses: actions/checkout@v2 - - - name: Generate code coverage - run: | - cargo tarpaulin --verbose --workspace --timeout 120 --out Xml --exclude e2e - - - name: Upload to codecov.io - uses: codecov/codecov-action@v2 - with: - token: ${{secrets.CODECOV_TOKEN}} - fail_ci_if_error: true diff --git a/.github/workflows/e2e.yml b/.github/workflows/e2e.yml index 3ef8b00..fd0c5c4 100644 --- a/.github/workflows/e2e.yml +++ b/.github/workflows/e2e.yml @@ -2,17 +2,11 @@ name: E2E Integration Tests on: pull_request: - push: - branches: - - main jobs: test: name: E2E tests runs-on: ubuntu-latest - env: - GAS_OUT_DIR: gas_reports - GAS_LIMIT: 75000000 steps: - name: Checkout sources uses: actions/checkout@v3 @@ -34,9 +28,6 @@ jobs: artifacts/ key: ${{ runner.os }}-cargo-with-artifacts-${{ hashFiles('**/Cargo.lock') }} - - name: Get mainnet GAS_LIMIT - run: echo "MAINNET_GAS_LIMIT=$(curl -s https://rpc.stargaze-apis.com/consensus_params | jq -r '.result.consensus_params.block.max_gas')" >> $GITHUB_ENV - - uses: extractions/setup-just@v1 - name: Download artifacts diff --git a/justfile b/justfile index 3d3c670..660b31b 100644 --- a/justfile +++ b/justfile @@ -38,7 +38,7 @@ deploy-local: -p 26657:26657 \ -p 9090:9090 \ --mount type=volume,source=stargaze_data,target=/root \ - publicawesome/stargaze:11.0.0 /data/entry-point.sh $TEST_ADDRS + publicawesome/stargaze:12.0.0-alpha.1 /data/entry-point.sh $TEST_ADDRS deploy-local-arm: #!/usr/bin/env bash @@ -55,7 +55,7 @@ deploy-local-arm: -p 9090:9090 \ --mount type=volume,source=stargaze_data,target=/root \ --platform linux/amd64 \ - publicawesome/stargaze:11.0.0 /data/entry-point.sh $TEST_ADDRS + publicawesome/stargaze:12.0.0-alpha.1 /data/entry-point.sh $TEST_ADDRS e2e-test: #!/usr/bin/env bash diff --git a/typescript/packages/e2e-tests/tests/infinity/infinityBuilder.test.ts b/typescript/packages/e2e-tests/tests/infinity/infinityBuilder.test.ts index 479882c..da5826c 100644 --- a/typescript/packages/e2e-tests/tests/infinity/infinityBuilder.test.ts +++ b/typescript/packages/e2e-tests/tests/infinity/infinityBuilder.test.ts @@ -1,7 +1,4 @@ import Context, { CONTRACT_MAP } from '../setup/context' -import { getQueryClient } from '../utils/client' -import { approveNft, createMinter, mintNft } from '../utils/nft' -import { sleep } from '../utils/sleep' import _ from 'lodash' describe('InfinityBuilder', () => { diff --git a/typescript/packages/e2e-tests/tests/infinity/infinityPair.test.ts b/typescript/packages/e2e-tests/tests/infinity/infinityPair.test.ts index ac25d0c..efd5e5f 100644 --- a/typescript/packages/e2e-tests/tests/infinity/infinityPair.test.ts +++ b/typescript/packages/e2e-tests/tests/infinity/infinityPair.test.ts @@ -1,14 +1,13 @@ -import { CosmWasmClient, SigningCosmWasmClient } from '@cosmjs/cosmwasm-stargate' +import { CosmWasmClient } from '@cosmjs/cosmwasm-stargate' import { toUtf8 } from '@cosmjs/encoding' import { denom } from '../../configs/chain_config.json' import Context, { CONTRACT_MAP } from '../setup/context' import { getQueryClient } from '../utils/client' -import { approveNft, createMinter, mintNft } from '../utils/nft' +import { createMinter, mintNft, mintNfts } from '../utils/nft' import { contracts } from '@stargazezone/infinity-types' import { ExecuteMsg as InfinityFactoryExecuteMsg } from '@stargazezone/infinity-types/lib/InfinityFactory.types' import { GlobalConfigForAddr } from '@stargazezone/infinity-types/lib/InfinityGlobal.types' import { ExecuteMsg as InfinityPairExecuteMsg, QuoteSummary } from '@stargazezone/infinity-types/lib/InfinityPair.types' -import { InfinityRouterClient } from '@stargazezone/infinity-types/lib/InfinityRouter.client' import { MsgExecuteContract } from 'cosmjs-types/cosmwasm/wasm/v1/tx' import _ from 'lodash' @@ -49,12 +48,8 @@ describe('InfinityPair', () => { const lpAssetRecipient = context.getTestUser(lpAssetRecipientName) let numNfts = 3 - let tokenIds = [] - for (let i = 0; i < numNfts; i++) { - let tokenId = await mintNft(context, creator.client, creator.address, liquidityProvider.address) - // await approveNft(seller.client, seller.address, collectionAddress, tokenId, reserveAuctionAddress) - tokenIds.push(tokenId) - } + let tokenIds = await mintNfts(context, globalConfig, numNfts, liquidityProvider) + let infinityFactoryQueryClient = new InfinityFactoryQueryClient(queryClient, globalConfig.infinity_factory) let nextPairResponse = await infinityFactoryQueryClient.nextPair({ sender: liquidityProvider.address }) pairAddress = nextPairResponse.pair @@ -179,8 +174,7 @@ describe('InfinityPair', () => { let sellToPairQuoteSummary = pair.internal.sell_to_pair_quote_summary as QuoteSummary expect(sellToPairQuoteSummary).toBeDefined() - let tokenId = await mintNft(context, creator.client, creator.address, swapper.address) - await approveNft(swapper.client, swapper.address, collectionAddress, tokenId, pairAddress) + let tokenId = await mintNft(context, globalConfig, swapper, pairAddress) let infinityPairClient = new InfinityPairClient(swapper.client, swapper.address, pairAddress) @@ -219,8 +213,7 @@ describe('InfinityPair', () => { parseInt(buyFromPairQuoteSummary.seller_amount) + parseInt(buyFromPairQuoteSummary.swap?.amount || '0') - let tokenId = await mintNft(context, creator.client, creator.address, swapper.address) - await approveNft(swapper.client, swapper.address, collectionAddress, tokenId, pairAddress) + let tokenId = await mintNft(context, globalConfig, swapper, pairAddress) let infinityPairClient = new InfinityPairClient(swapper.client, swapper.address, pairAddress) diff --git a/typescript/packages/e2e-tests/tests/infinity/infinityRouter.test.ts b/typescript/packages/e2e-tests/tests/infinity/infinityRouter.test.ts index fa775c3..08bbe48 100644 --- a/typescript/packages/e2e-tests/tests/infinity/infinityRouter.test.ts +++ b/typescript/packages/e2e-tests/tests/infinity/infinityRouter.test.ts @@ -1,16 +1,12 @@ -import { CosmWasmClient, SigningCosmWasmClient } from '@cosmjs/cosmwasm-stargate' -import { toUtf8 } from '@cosmjs/encoding' +import { CosmWasmClient } from '@cosmjs/cosmwasm-stargate' import { denom } from '../../configs/chain_config.json' import Context, { CONTRACT_MAP } from '../setup/context' import { getQueryClient } from '../utils/client' import { createPair } from '../utils/infinity' -import { approveNft, createMinter, mintNft } from '../utils/nft' +import { createMinter, mintNfts } from '../utils/nft' import { contracts } from '@stargazezone/infinity-types' -import { ExecuteMsg as InfinityFactoryExecuteMsg } from '@stargazezone/infinity-types/lib/InfinityFactory.types' import { GlobalConfigForAddr } from '@stargazezone/infinity-types/lib/InfinityGlobal.types' -import { ExecuteMsg as InfinityPairExecuteMsg } from '@stargazezone/infinity-types/lib/InfinityPair.types' import { InfinityRouterClient } from '@stargazezone/infinity-types/lib/InfinityRouter.client' -import { MsgExecuteContract } from 'cosmjs-types/cosmwasm/wasm/v1/tx' import _ from 'lodash' const { InfinityGlobalQueryClient } = contracts.InfinityGlobal @@ -98,12 +94,7 @@ describe('InfinityRouter', () => { limit, }) - let tokenIds = [] - for (let i = 0; i < limit; i++) { - let tokenId = await mintNft(context, creator.client, creator.address, swapper.address) - await approveNft(swapper.client, swapper.address, collectionAddress, tokenId, globalConfig.infinity_router) - tokenIds.push(tokenId) - } + let tokenIds = await mintNfts(context, globalConfig, limit, swapper, globalConfig.infinity_router) let swapperBalanceBefore = await queryClient.getBalance(swapper.address, denom) diff --git a/typescript/packages/e2e-tests/tests/utils/infinity.ts b/typescript/packages/e2e-tests/tests/utils/infinity.ts index 917a661..01337c4 100644 --- a/typescript/packages/e2e-tests/tests/utils/infinity.ts +++ b/typescript/packages/e2e-tests/tests/utils/infinity.ts @@ -2,7 +2,7 @@ import { toUtf8 } from '@cosmjs/encoding' import { denom } from '../../configs/chain_config.json' import Context from '../setup/context' import { getQueryClient } from '../utils/client' -import { mintNft } from '../utils/nft' +import { mintNfts } from '../utils/nft' import { contracts } from '@stargazezone/infinity-types' import { ExecuteMsg as InfinityFactoryExecuteMsg } from '@stargazezone/infinity-types/lib/InfinityFactory.types' import { GlobalConfigForAddr } from '@stargazezone/infinity-types/lib/InfinityGlobal.types' @@ -27,11 +27,7 @@ export const createPair = async ( const queryClient = await getQueryClient() - let tokenIds = [] - for (let i = 0; i < numNfts; i++) { - let tokenId = await mintNft(context, creator.client, creator.address, liquidityProvider.address) - tokenIds.push(tokenId) - } + let tokenIds = await mintNfts(context, globalConfig, numNfts, liquidityProvider) let infinityFactoryQueryClient = new InfinityFactoryQueryClient(queryClient, globalConfig.infinity_factory) diff --git a/typescript/packages/e2e-tests/tests/utils/nft.ts b/typescript/packages/e2e-tests/tests/utils/nft.ts index 9c06b37..5b65198 100644 --- a/typescript/packages/e2e-tests/tests/utils/nft.ts +++ b/typescript/packages/e2e-tests/tests/utils/nft.ts @@ -1,11 +1,14 @@ -import { SigningCosmWasmClient } from '@cosmjs/cosmwasm-stargate' -import Context, { CONTRACT_MAP } from '../setup/context' +import { CosmWasmClient, SigningCosmWasmClient } from '@cosmjs/cosmwasm-stargate' +import { toUtf8 } from '@cosmjs/encoding' +import Context, { CONTRACT_MAP, TestUser } from '../setup/context' import { Sg2ExecuteMsgForVendingMinterInitMsgExtension } from '../types/vendingFactory' import { getQueryClient, getSigningClient } from './client' import { getFutureTimestamp, nanoToMs, waitUntil } from './datetime' import { sleep } from './sleep' +import { GlobalConfigForAddr } from '@stargazezone/infinity-types/lib/InfinityGlobal.types' import { ExecuteMsg as BaseFactoryExecuteMsg } from '@stargazezone/launchpad/src/BaseFactory.types' import assert from 'assert' +import { MsgExecuteContract } from 'cosmjs-types/cosmwasm/wasm/v1/tx' import _ from 'lodash' export const createMinter = async (context: Context) => { @@ -64,63 +67,115 @@ export const createMinter = async (context: Context) => { context.addContractAddress(CONTRACT_MAP.VENDING_MINTER, minterAddress) context.addContractAddress(CONTRACT_MAP.SG721_BASE, collectionAddress) + await waitForMinter(queryClient, minterAddress) + return collectionAddress } -export const mintNft = async ( - context: Context, - signingClient: SigningCosmWasmClient, - sender: string, - recipientAddress: string, -): Promise => { - const queryClient = await getQueryClient() - - let vendingFactoryAddress = context.getContractAddress(CONTRACT_MAP.VENDING_FACTORY) - let { params: factoryParams } = await queryClient.queryContractSmart(vendingFactoryAddress, { - params: {}, +export const waitForMinter = async (queryClient: CosmWasmClient, vendingMinterAddress: string) => { + let minterConfig = await queryClient.queryContractSmart(vendingMinterAddress, { + config: {}, }) + await waitUntil(new Date(nanoToMs(minterConfig.start_time) + 2000)) +} + +export const mintNfts = async ( + context: Context, + globalConfig: GlobalConfigForAddr, + numNfts: number, + recipient: TestUser, + approveAddress?: string, +): Promise => { + let queryClient = await getQueryClient() + let creator = context.getTestUser('user1') let vendingMinterAddress = context.getContractAddress(CONTRACT_MAP.VENDING_MINTER) let minterConfig = await queryClient.queryContractSmart(vendingMinterAddress, { config: {}, }) - await waitUntil(new Date(nanoToMs(minterConfig.start_time) + 2000)) - let collectionAddress = minterConfig.sg721_address - let mintMsg = { mint: {} } - // let mintPrice = (factoryParams.mint_fee_bps * factoryParams.min_mint_price.amount) / 10000 - let mintExecuteResult = await signingClient.execute(sender, vendingMinterAddress, mintMsg, 'auto', 'mint-nft', [ - minterConfig.mint_price, - ]) - - let tokenId: string = '' - for (const idx in mintExecuteResult.events) { - { - const event = mintExecuteResult.events[idx] - let tokenIdAttribute = _.find(event.attributes, (attribute) => attribute.key === 'token_id') - if (tokenIdAttribute) { - tokenId = tokenIdAttribute.value - break - } - } + let encodedMessages: any[] = [] + + for (let i = 0; i < numNfts; i++) { + let mintMsg = { mint: {} } + encodedMessages.push({ + typeUrl: '/cosmwasm.wasm.v1.MsgExecuteContract', + value: MsgExecuteContract.fromPartial({ + sender: creator.address, + contract: vendingMinterAddress, + msg: toUtf8(JSON.stringify(mintMsg)), + funds: [minterConfig.mint_price], + }), + }) } - assert(tokenId, 'token_id not found in wasm event attributes') - - // Transfer NFT to recipient - if (sender !== recipientAddress) { - let transferMsg = { transfer_nft: { recipient: recipientAddress, token_id: tokenId } } - let transferExecuteResult = await signingClient.execute( - sender, - collectionAddress, - transferMsg, - 'auto', - 'transfer-nft', - ) + + let deliverTxResponse = await creator.client.signAndBroadcast(creator.address, encodedMessages, 'auto') + + let tokenIds: Set = new Set() + _.forEach( + _.filter(deliverTxResponse.events, (event) => event.type === 'wasm'), + (event) => { + _.forEach(event.attributes, (attribute) => { + if (attribute.key == 'token_id') { + tokenIds.add(attribute.value) + } + }) + }, + ) + + encodedMessages = [] + + tokenIds.forEach((tokenId) => { + let transferMsg = { transfer_nft: { recipient: recipient.address, token_id: tokenId } } + encodedMessages.push({ + typeUrl: '/cosmwasm.wasm.v1.MsgExecuteContract', + value: MsgExecuteContract.fromPartial({ + sender: creator.address, + contract: collectionAddress, + msg: toUtf8(JSON.stringify(transferMsg)), + funds: [], + }), + }) + }) + + if (encodedMessages.length > 0) { + await creator.client.signAndBroadcast(creator.address, encodedMessages, 'auto') } - return tokenId + encodedMessages = [] + + if (approveAddress) { + tokenIds.forEach((tokenId) => { + let approveMsg = { approve: { spender: approveAddress, token_id: tokenId } } + encodedMessages.push({ + typeUrl: '/cosmwasm.wasm.v1.MsgExecuteContract', + value: MsgExecuteContract.fromPartial({ + sender: recipient.address, + contract: collectionAddress, + msg: toUtf8(JSON.stringify(approveMsg)), + funds: [], + }), + }) + }) + } + + if (encodedMessages.length > 0) { + await recipient.client.signAndBroadcast(recipient.address, encodedMessages, 'auto') + } + + return [...tokenIds] +} + +export const mintNft = async ( + context: Context, + globalConfig: GlobalConfigForAddr, + recipient: TestUser, + approveAddress?: string, +): Promise => { + let tokenIds = await mintNfts(context, globalConfig, 1, recipient, approveAddress) + return tokenIds[0] } export const approveNft = async (