diff --git a/.github/actions/build-anchor/action.yaml b/.github/actions/build-anchor/action.yaml new file mode 100644 index 00000000..a32da816 --- /dev/null +++ b/.github/actions/build-anchor/action.yaml @@ -0,0 +1,49 @@ +name: "Anchor Build" +description: "Build a program using Anchor" +inputs: + program: + description: "The program to build" + required: true + program_id: + description: "The program address" + required: true + replace_program_id: + description: "Whether to replace current program id with `program_id` in lib.rs, e.g. when deploying to a staging address" + +runs: + using: "composite" + steps: + - name: Cache Anchor build + uses: actions/cache@v3 + id: cache-anchor-build + if: env.CACHE != 'true' || steps.cache-anchor-build.outputs.cache-hit != 'true' + with: + path: | + ./target/ + key: build-${{ runner.os }}-${{env.ANCHOR_VERSION}}-${{ hashFiles(format('./programs/{0}/**', inputs.program)) }}-${{ inputs.program }}-${{ inputs.staging }} + + - name: Replace program ID if needed + if: inputs.replace_program_id == 'true' + env: + PROGRAM_ID: ${{ inputs.program_id }} + run: | + sed -i "s/declare_id!(\".*\")/declare_id!(\"${PROGRAM_ID}\")/g" ./programs/${{ inputs.program }}/src/lib.rs + shell: bash + + - name: Build with Anchor + run: ~/.cargo/bin/anchor build -p ${{ inputs.program }} + shell: bash + + - name: Store .so files + uses: actions/upload-artifact@v3 + with: + name: ${{inputs.program}}-so + path: | + ./target/deploy/${{inputs.program}}.so + + - name: Store IDL files + uses: actions/upload-artifact@v3 + with: + name: ${{inputs.program}}-idl + path: | + ./target/idl/${{inputs.program}}.json diff --git a/.github/actions/deploy-anchor/action.yaml b/.github/actions/deploy-anchor/action.yaml new file mode 100644 index 00000000..d7cff5d3 --- /dev/null +++ b/.github/actions/deploy-anchor/action.yaml @@ -0,0 +1,49 @@ +name: "Deploy Program with Anchor" +description: "Deploy a program to a cluster using Anchor" + +inputs: + program: + description: "The program to build" + required: true + program_id: + description: "The program address" + required: true + rpc_url: + description: "The RPC url for the cluster to deploy to" + required: true + upgrade_authority_keypair: + description: "The keypair to use as the upgrade authority" + required: true + +runs: + using: "composite" + steps: + - name: Save Upgrade Authority keypair on disk + run: | + echo $UPGRADE_AUTHORITY_KEYPAIR > ./upgrade-authority.json && chmod 600 ./upgrade-authority.json + shell: bash + env: + UPGRADE_AUTHORITY_KEYPAIR: ${{ inputs.upgrade_authority_keypair }} + + - name: Deploy .so with Anchor + run: | + ~/.cargo/bin/anchor upgrade --program-id $PROGRAM_ID --provider.cluster $RPC_URL --provider.wallet ./upgrade-authority.json ./target/deploy/${PROGRAM_NAME}.so + shell: bash + env: + RPC_URL: ${{ inputs.rpc_url }} + PROGRAM_NAME: ${{ inputs.program }} + PROGRAM_ID: ${{ inputs.program_id }} + + - name: Deploy IDL with Anchor + run: | + anchor idl upgrade --provider.cluster $RPC_URL --provider.wallet ./upgrade-authority.json --filepath target/idl/${PROGRAM_NAME}.json $PROGRAM_ID + shell: bash + env: + RPC_URL: ${{ inputs.rpc_url }} + PROGRAM_NAME: ${{ inputs.program }} + PROGRAM_ID: ${{ inputs.program_id }} + + - name: Remove Upgrade Authority keypair from disk + run: | + rm ./upgrade-authority.json + shell: bash diff --git a/.github/workflows/check-staging-deployer-balance.yaml b/.github/workflows/check-staging-deployer-balance.yaml new file mode 100644 index 00000000..5831a757 --- /dev/null +++ b/.github/workflows/check-staging-deployer-balance.yaml @@ -0,0 +1,49 @@ +# Checks that the balance of the Solana public key +# associated with the UPGRADE_AUTHORITY_KEYPAIR_STAGING is greater than +# the estimated balance required to deploy a program. +name: Check Staging Deployer Balance + +on: + pull_request: + branches: + - main + paths: + - 'programs/squads_multisig_program/**' + +jobs: + check-balance: + if: contains(github.event.pull_request.labels.*.name, 'deploy-staging') + runs-on: ubuntu-latest + steps: + - name: Checkout + uses: actions/checkout@v3 + + - name: Setup Solana + uses: ./.github/actions/setup-solana/ + + - name: Check Solana Balance + env: + UPGRADE_AUTHORITY_KEYPAIR: ${{ secrets.UPGRADE_AUTHORITY_KEYPAIR_STAGING }} + RPC_URL: ${{ secrets.RPC_URL_MAINNET }} + run: | + # Write the keypair to a file so it can be used with solana-keygen + echo $UPGRADE_AUTHORITY_KEYPAIR > ./upgrade-authority.json && chmod 600 ./upgrade-authority.json + + # Extract the public key from the keypair file + PUBLIC_KEY=$(solana-keygen pubkey ./upgrade-authority.json) + + # Remove the keypair file + rm ./upgrade-authority.json + + # Fetch the balance of the public key + BALANCE=$(solana balance $PUBLIC_KEY --url $RPC_URL | awk '{print $1}') + + MIN_BALANCE=10 + + # Check if the balance is less than the minimum balance using floating point comparison with `bc` + if (( $(echo "$BALANCE < $MIN_BALANCE" | bc -l) )); then + echo "Balance of $PUBLIC_KEY is $BALANCE, which is less than the minimum balance of $MIN_BALANCE" + exit 1 + fi + + echo "Balance of $PUBLIC_KEY is $BALANCE SOL" diff --git a/.github/workflows/publish-staging.yaml b/.github/workflows/publish-staging.yaml new file mode 100644 index 00000000..631549bb --- /dev/null +++ b/.github/workflows/publish-staging.yaml @@ -0,0 +1,25 @@ +name: Publish to Staging + +on: + pull_request: + types: + - closed + branches: + - main + paths: + - 'programs/squads_multisig_program/**' + +jobs: + publish-staging: + if: github.event.pull_request.merged == true && contains(github.event.pull_request.labels.*.name, 'deploy-staging') + uses: ./.github/workflows/reusable-publish-staging.yaml + with: + cache: true + solana_cli_version: 1.17.0 + anchor_version: 0.29.0 + program: "squads_multisig_program" + program_id: "STAG3xkFMyVK3sRtQhipsKuLpRGbgospDpVdNyJqDpS" + replace_program_id: true + secrets: + RPC_URL: ${{ secrets.RPC_URL_MAINNET }} + UPGRADE_AUTHORITY_KEYPAIR: ${{ secrets.UPGRADE_AUTHORITY_KEYPAIR_STAGING }} diff --git a/.github/workflows/reusable-publish-staging.yaml b/.github/workflows/reusable-publish-staging.yaml new file mode 100644 index 00000000..4d5282d3 --- /dev/null +++ b/.github/workflows/reusable-publish-staging.yaml @@ -0,0 +1,73 @@ +name: Reusable Publish to Staging + +on: + workflow_call: + inputs: + cache: + required: true + type: boolean + solana_cli_version: + required: true + type: string + anchor_version: + required: true + type: string + program: + description: "The program to build" + required: true + type: string + program_id: + description: "The program address" + required: true + type: string + replace_program_id: + description: "Whether to replace current program id with `program_id` in lib.rs, e.g. when deploying to a staging address" + type: boolean + + secrets: + RPC_URL: + required: true + UPGRADE_AUTHORITY_KEYPAIR: + required: true + +env: + CACHE: inputs.cache + SOLANA_CLI_VERSION: ${{ inputs.solana_cli_version }} + NODE_VERSION: ${{ inputs.node_version }} + CARGO_PROFILE: ${{ inputs.cargo_profile }} + ANCHOR_VERSION: ${{ inputs.anchor_version }} + +jobs: + publish: + name: Publish + runs-on: ubuntu-latest + timeout-minutes: 30 + steps: + - name: Checkout + uses: actions/checkout@v3 + + - name: Setup Rust + uses: ./.github/actions/setup-rust/ + + - name: Setup Solana + uses: ./.github/actions/setup-solana/ + + - name: Setup Anchor + uses: ./.github/actions/setup-anchor/ + + - name: Build Anchor + uses: ./.github/actions/build-anchor/ + with: + program: ${{ inputs.program }} + program_id: ${{ inputs.program_id }} + replace_program_id: ${{ inputs.replace_program_id }} + + - name: Deploy with Anchor + uses: ./.github/actions/deploy-anchor/ + with: + program: ${{ inputs.program }} + program_id: ${{ inputs.program_id }} + rpc_url: ${{ secrets.RPC_URL }} + upgrade_authority_keypair: ${{ secrets.UPGRADE_AUTHORITY_KEYPAIR }} + + diff --git a/package.json b/package.json index d6b36a59..5f182336 100644 --- a/package.json +++ b/package.json @@ -7,7 +7,7 @@ "build": "turbo run build", "test": "turbo run build && anchor test", "ts": "turbo run ts && yarn tsc --noEmit", - "deploy": "anchor deploy --provider.cluster mainnet-beta --provider.wallet .keys/upgrade-authority.json --program-name multisig" + "deploy": "anchor deploy --provider.cluster mainnet-beta --provider.wallet .keys/upgrade-authority-staging-and-devnet.json --program-name multisig" }, "devDependencies": { "@solana/spl-token": "*", diff --git a/programs/squads_multisig_program/Cargo.toml b/programs/squads_multisig_program/Cargo.toml index 170fe2d0..ace8ace5 100644 --- a/programs/squads_multisig_program/Cargo.toml +++ b/programs/squads_multisig_program/Cargo.toml @@ -20,3 +20,4 @@ default = [] anchor-lang = { version = "=0.29.0", features = ["allow-missing-optionals"] } anchor-spl = { version="=0.29.0", features=["token"] } solana-security-txt = "1.1.1" +# \ No newline at end of file diff --git a/sdk/multisig/.solitarc.js b/sdk/multisig/.solitarc.js index 1abe89a2..f4b37a95 100644 --- a/sdk/multisig/.solitarc.js +++ b/sdk/multisig/.solitarc.js @@ -39,6 +39,7 @@ const keypair = loadKeypairFromFile( ); const pubkey = keypair.publicKey.toBase58(); +console.log("Generating SDK for Program ID:", pubkey); module.exports = { idlGenerator: "anchor",