Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
23 changes: 22 additions & 1 deletion .github/workflows/pr-multisig-v1-smoke.yml
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ jobs:
multisig-v1-smoke:
if: github.repository == 'MeshJS/multisig'
runs-on: ubuntu-latest
timeout-minutes: 45
timeout-minutes: 120
env:
CI_JWT_SECRET: ${{ secrets.CI_JWT_SECRET }}
CI_MNEMONIC_1: ${{ secrets.CI_MNEMONIC_1 }}
Expand Down Expand Up @@ -63,6 +63,27 @@ jobs:
[[ -n "$CI_MNEMONIC_3" ]] || missing+=("CI_MNEMONIC_3")
[[ -n "$CI_BLOCKFROST_PREPROD_API_KEY" ]] || missing+=("CI_BLOCKFROST_PREPROD_API_KEY")

route_scenarios=",${CI_ROUTE_SCENARIOS//[[:space:]]/},"
default_route_chain=false
if [[ -z "${CI_ROUTE_SCENARIOS//[[:space:]]/}" ]]; then
default_route_chain=true
fi

scenario_enabled() {
local scenario_id="$1"
[[ "$default_route_chain" == "true" || "$route_scenarios" == *",$scenario_id,"* ]]
}

if scenario_enabled "scenario.drep-certificates" || scenario_enabled "scenario.proxy-full-lifecycle"; then
[[ -n "$CI_DREP_ANCHOR_URL" ]] || missing+=("CI_DREP_ANCHOR_URL")
fi
if scenario_enabled "scenario.drep-certificates"; then
[[ -n "$CI_DREP_ANCHOR_JSON" ]] || missing+=("CI_DREP_ANCHOR_JSON")
fi
if scenario_enabled "scenario.stake-certificates"; then
[[ -n "$CI_STAKE_POOL_ID_HEX" ]] || missing+=("CI_STAKE_POOL_ID_HEX")
fi

if [[ "${#missing[@]}" -gt 0 ]]; then
echo "Missing required secrets: ${missing[*]}"
echo "Set these in repo settings before running PR multisig smoke workflow."
Expand Down
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@
"prestart": "prisma migrate deploy",
"start": "next start",
"test": "jest",
"test:bot:unit": "jest src/__tests__/botAuth.test.ts src/__tests__/botMe.test.ts src/__tests__/createWallet.bot.test.ts src/__tests__/walletIds.bot.test.ts src/__tests__/pendingTransactions.bot.test.ts src/__tests__/freeUtxos.bot.test.ts src/__tests__/addTransaction.bot.test.ts src/__tests__/nativeScript.bot.test.ts src/__tests__/governanceActiveProposals.test.ts src/__tests__/botBallotsUpsert.test.ts src/__tests__/signTransaction.bot.test.ts src/__tests__/submitDatum.bot.test.ts src/__tests__/resolveUtxoRefsFromChain.test.ts src/__tests__/resolveDRepAnchorFromUrl.test.ts src/__tests__/normalizePoolId.test.ts",
"test:bot:unit": "jest src/__tests__/botAuth.test.ts src/__tests__/botMe.test.ts src/__tests__/createWallet.bot.test.ts src/__tests__/walletIds.bot.test.ts src/__tests__/pendingTransactions.bot.test.ts src/__tests__/freeUtxos.bot.test.ts src/__tests__/addTransaction.bot.test.ts src/__tests__/nativeScript.bot.test.ts src/__tests__/governanceActiveProposals.test.ts src/__tests__/botBallotsUpsert.test.ts src/__tests__/signTransaction.bot.test.ts src/__tests__/submitDatum.bot.test.ts src/__tests__/resolveUtxoRefsFromChain.test.ts src/__tests__/resolveDRepAnchorFromUrl.test.ts src/__tests__/normalizePoolId.test.ts src/__tests__/createPendingMultisigTransaction.test.ts src/__tests__/proxyUtxos.test.ts src/__tests__/proxyTxBuilders.test.ts src/__tests__/proxySetup.bot.test.ts src/__tests__/proxyCleanup.bot.test.ts src/__tests__/proxyAccess.test.ts src/__tests__/proxySetupFinalization.test.ts src/__tests__/proxyCleanupFinalization.test.ts src/__tests__/proxyCiPreflight.test.ts src/__tests__/proxyCiOrphanAdoption.test.ts src/__tests__/proxyCiChainRecovery.test.ts src/__tests__/proxyBotSelection.test.ts src/__tests__/proxyCleanupRuntime.test.ts src/__tests__/ciSigningSelection.test.ts src/__tests__/ciScenarioManifest.test.ts",
"test:bot:integration": "jest src/__tests__/botApi.integration.test.ts --runInBand",
"test:bot": "npm run test:bot:unit && npm run test:bot:integration",
"test:watch": "jest --watch",
Expand Down
40 changes: 37 additions & 3 deletions scripts/bot-ref/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,7 @@ npm install
```bash
curl -sS -X POST http://localhost:3000/api/v1/botRegister \
-H "Content-Type: application/json" \
-d '{"name":"Reference Bot","paymentAddress":"addr1_xxx","scopes":["multisig:read"]}'
-d '{"name":"Reference Bot","paymentAddress":"addr1_xxx","requestedScopes":["multisig:read","multisig:sign"]}'
```

Response includes `pendingBotId` and `claimCode`.
Expand Down Expand Up @@ -159,10 +159,10 @@ If `numRequiredSigners > 1`, the response is a pending `Transaction` row; co-sig

### 9. DRep certificate (register / retire)

Also requires **multisig:sign**. **Summon** wallets are rejected; **legacy** wallets use payment-script DRep derivation (same as the app). For `register`, set `anchorUrl` to an HTTPS URL returning JSON; the server fetches it, computes `hashDrepAnchor`, and optionally verifies `anchorDataHash`.
Also requires **multisig:sign**. **Summon** wallets are rejected; **legacy** wallets use payment-script DRep derivation (same as the app). For `register`, send both `anchorUrl` and `anchorJson`; the server does not fetch the URL and computes `hashDrepAnchor(anchorJson)` from the object you provide.

```bash
# drep-register.json — anchorUrl required for register
# drep-register.json — anchorUrl and anchorJson required for register
npx tsx bot-client.ts drepCert drep-register.json
```

Expand Down Expand Up @@ -194,6 +194,40 @@ BOT_TOKEN='...' BOT_CONFIG_PATH=bot-config.json npx tsx bot-client.ts walletIds

The reference client only uses **bot-key auth** (POST /api/v1/botAuth). Wallet-based auth (getNonce + sign + authSigner) would require a real Cardano signer; implement that in your bot if needed.

## Proxy bot API

Proxy routes use the normal pending multisig flow and require `multisig:sign` plus **cosigner** access for mutating calls. `GET /api/v1/proxies` and `GET /api/v1/proxyDRepInfo` allow bot observer access. The reference CLI does not wrap these routes yet; call them directly with `BOT_TOKEN`.

All proxy transaction builders accept UTxO references, not raw UTxO JSON:

```json
{ "txHash": "<transaction hash>", "outputIndex": 0 }
```

Use `GET /api/v1/freeUtxos?walletId=...&address=...&fresh=true` to select wallet inputs. `collateralRef` must be an ADA-only UTxO with at least 5 ADA at the bot payment address. Proxy actions also require a wallet input containing the proxy auth token, returned from setup/finalization metadata as `authTokenId`.

### Setup and finalize

1. `POST /api/v1/proxySetup` with `walletId`, `address`, `utxoRefs`, `collateralRef`, optional `initialProxyLovelace`, and optional `description`.
2. Sign the returned pending transaction with the required wallet signers. Proxy transactions are persisted with no initial signed addresses, so the proposer still needs to sign through `signTransaction`.
3. After the setup transaction is confirmed, call `POST /api/v1/proxySetupFinalize` with `walletId`, `address`, `txHash`, `proxyAddress`, `authTokenId`, and `paramUtxo` from the setup response. The server validates the confirmed setup outputs and creates or reactivates the `Proxy` row.
4. `GET /api/v1/proxies?walletId=...&address=...` lists active confirmed proxies.

### Spend, DRep, and vote

- `POST /api/v1/proxySpend`: sends proxy-held assets to `outputs[]`. If `proxyUtxoRefs` is omitted, the server selects proxy-address UTxOs sufficient for the requested outputs plus fee buffer.
- `POST /api/v1/proxyDRepCertificate`: `action` is `register`, `update`, or `deregister`. `register` and `update` require both `anchorUrl` and `anchorJson`; the server computes the anchor hash from `anchorJson`.
- `GET /api/v1/proxyDRepInfo`: returns `{ active, dRepId }` for the proxy script DRep credential.
- `POST /api/v1/proxyVote`: votes as the proxy DRep. Each vote uses `proposalId` in `<txHash>#<certIndex>` form and `voteKind` of `Yes`, `No`, or `Abstain`.

### Cleanup

`POST /api/v1/proxyCleanup` is safe to call repeatedly during lifecycle cleanup:

1. If the proxy address still has UTxOs, it returns cleanup phase `sweep`; sign and submit that transaction, then wait until the proxy address is empty. When `proxyUtxoRefs` is provided, it must include every currently visible proxy UTxO.
2. Call `POST /api/v1/proxyCleanup` again. When cleanup phase is `burn`, sign and submit the burn transaction.
3. After burn confirmation, call `POST /api/v1/proxyCleanupFinalize` with the confirmed burn `txHash`. The server validates that the auth token was spent and not recreated, that the proxy address has no UTxOs, and deactivates the proxy row unless `deactivateProxy` is `false`.

## Governance bot flow

For governance automation, request and approve these bot scopes during register/claim:
Expand Down
Loading
Loading