Xc admin/add executor (#466)
* Add encoder * Update test * Cleanup * More cleanup * Add executor * Update comment * Update package lock * Cleanup * Fix bug and error handling
This commit is contained in:
parent
1153abe6ff
commit
80fe230563
|
@ -5,6 +5,7 @@ repos:
|
||||||
- id: trailing-whitespace
|
- id: trailing-whitespace
|
||||||
- id: end-of-file-fixer
|
- id: end-of-file-fixer
|
||||||
- id: check-added-large-files
|
- id: check-added-large-files
|
||||||
|
exclude: package-lock.json
|
||||||
# Hook to format many type of files in the repo
|
# Hook to format many type of files in the repo
|
||||||
# including solidity contracts.
|
# including solidity contracts.
|
||||||
- repo: https://github.com/pre-commit/mirrors-prettier
|
- repo: https://github.com/pre-commit/mirrors-prettier
|
||||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,27 @@
|
||||||
|
{
|
||||||
|
"name": "crank-executor",
|
||||||
|
"version": "0.0.0",
|
||||||
|
"description": "A crank to executed all executeReady multisig transaction",
|
||||||
|
"author": "",
|
||||||
|
"homepage": "https://github.com/pyth-network/pyth-crosschain",
|
||||||
|
"license": "ISC",
|
||||||
|
"main": "src/index.ts",
|
||||||
|
"repository": {
|
||||||
|
"type": "git",
|
||||||
|
"url": "git+https://github.com/pyth-network/pyth-crosschain.git"
|
||||||
|
},
|
||||||
|
"bugs": {
|
||||||
|
"url": "https://github.com/pyth-network/pyth-crosschain/issues"
|
||||||
|
},
|
||||||
|
"scripts": {
|
||||||
|
"build": "tsc",
|
||||||
|
"format": "prettier --write \"src/**/*.ts\""
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"@coral-xyz/anchor": "^0.26.0",
|
||||||
|
"@pythnetwork/client": "^2.9.0",
|
||||||
|
"@solana/web3.js": "^1.73.0",
|
||||||
|
"@sqds/mesh": "^1.0.6",
|
||||||
|
"ts-node": "^10.9.1"
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,80 @@
|
||||||
|
import {
|
||||||
|
Commitment,
|
||||||
|
Connection,
|
||||||
|
Keypair,
|
||||||
|
PublicKey,
|
||||||
|
SendTransactionError,
|
||||||
|
Transaction,
|
||||||
|
} from "@solana/web3.js";
|
||||||
|
import SquadsMesh, { DEFAULT_MULTISIG_PROGRAM_ID, getIxPDA } from "@sqds/mesh";
|
||||||
|
import * as fs from "fs";
|
||||||
|
import NodeWallet from "@project-serum/anchor/dist/cjs/nodewallet";
|
||||||
|
import { getProposals } from "xc-admin-common";
|
||||||
|
import BN from "bn.js";
|
||||||
|
import { AnchorProvider } from "@project-serum/anchor";
|
||||||
|
import {
|
||||||
|
getPythClusterApiUrl,
|
||||||
|
PythCluster,
|
||||||
|
} from "@pythnetwork/client/lib/cluster";
|
||||||
|
|
||||||
|
export function envOrErr(env: string): string {
|
||||||
|
const val = process.env[env];
|
||||||
|
if (!val) {
|
||||||
|
throw new Error(`environment variable "${env}" must be set`);
|
||||||
|
}
|
||||||
|
return String(process.env[env]);
|
||||||
|
}
|
||||||
|
|
||||||
|
const CLUSTER: string = envOrErr("CLUSTER");
|
||||||
|
const COMMITMENT: Commitment =
|
||||||
|
(process.env.COMMITMENT as Commitment) ?? "confirmed";
|
||||||
|
const VAULT: PublicKey = new PublicKey(envOrErr("VAULT"));
|
||||||
|
const KEYPAIR: Keypair = Keypair.fromSecretKey(
|
||||||
|
Uint8Array.from(JSON.parse(fs.readFileSync(envOrErr("WALLET"), "ascii")))
|
||||||
|
);
|
||||||
|
|
||||||
|
async function run() {
|
||||||
|
const squad = new SquadsMesh({
|
||||||
|
connection: new Connection(
|
||||||
|
getPythClusterApiUrl(CLUSTER as PythCluster),
|
||||||
|
COMMITMENT
|
||||||
|
),
|
||||||
|
wallet: new NodeWallet(KEYPAIR),
|
||||||
|
multisigProgramId: DEFAULT_MULTISIG_PROGRAM_ID,
|
||||||
|
});
|
||||||
|
const proposals = await getProposals(squad, VAULT, undefined, "executeReady");
|
||||||
|
for (const proposal of proposals) {
|
||||||
|
// If we have previously cancelled because the proposal was failing, don't attempt
|
||||||
|
if (proposal.cancelled.length == 0) {
|
||||||
|
for (
|
||||||
|
let i = proposal.executedIndex + 1;
|
||||||
|
i <= proposal.instructionIndex;
|
||||||
|
i++
|
||||||
|
) {
|
||||||
|
const transaction = new Transaction().add(
|
||||||
|
await squad.buildExecuteInstruction(
|
||||||
|
proposal.publicKey,
|
||||||
|
getIxPDA(proposal.publicKey, new BN(i), squad.multisigProgramId)[0]
|
||||||
|
)
|
||||||
|
);
|
||||||
|
|
||||||
|
try {
|
||||||
|
await new AnchorProvider(squad.connection, squad.wallet, {
|
||||||
|
commitment: COMMITMENT,
|
||||||
|
preflightCommitment: COMMITMENT,
|
||||||
|
}).sendAndConfirm(transaction, []);
|
||||||
|
} catch (error) {
|
||||||
|
// Mark the transaction as cancelled if we failed to run it
|
||||||
|
if (error instanceof SendTransactionError) {
|
||||||
|
await squad.cancelTransaction(proposal.publicKey);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
(async () => {
|
||||||
|
await run();
|
||||||
|
})();
|
|
@ -0,0 +1,16 @@
|
||||||
|
{
|
||||||
|
"compilerOptions": {
|
||||||
|
"declaration": true,
|
||||||
|
"target": "es2016",
|
||||||
|
"module": "commonjs",
|
||||||
|
"outDir": "lib",
|
||||||
|
"esModuleInterop": true,
|
||||||
|
"forceConsistentCasingInFileNames": true,
|
||||||
|
"strict": true,
|
||||||
|
"skipLibCheck": true,
|
||||||
|
"resolveJsonModule": true,
|
||||||
|
"noErrorTruncation": true
|
||||||
|
},
|
||||||
|
"include": ["src/**/*.ts"],
|
||||||
|
"exclude": ["src/__tests__/"]
|
||||||
|
}
|
|
@ -9,16 +9,18 @@ import BN from "bn.js";
|
||||||
import lodash from "lodash";
|
import lodash from "lodash";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Find all active proposals for vault `vault` using Squads client `squad`
|
* Find all proposals for vault `vault` using Squads client `squad`
|
||||||
* @param squad Squads client
|
* @param squad Squads client
|
||||||
* @param vault vault public key. It needs to exist in the instance of squads that `squad` is targeting
|
* @param vault vault public key. It needs to exist in the instance of squads that `squad` is targeting
|
||||||
* @param offset (optional) ignore all proposals with `proposal_index < offset`
|
* @param offset (optional) ignore all proposals with `proposal_index < offset`
|
||||||
|
* @param state filter by status
|
||||||
* @returns All the proposal accounts as `TransactionAccount`
|
* @returns All the proposal accounts as `TransactionAccount`
|
||||||
*/
|
*/
|
||||||
export async function getActiveProposals(
|
export async function getProposals(
|
||||||
squad: Squads,
|
squad: Squads,
|
||||||
vault: PublicKey,
|
vault: PublicKey,
|
||||||
offset: number = 1
|
offset: number = 1,
|
||||||
|
state: "active" | "executeReady" | "executed" | "all" = "all"
|
||||||
): Promise<TransactionAccount[]> {
|
): Promise<TransactionAccount[]> {
|
||||||
const msAccount = await squad.getMultisig(vault);
|
const msAccount = await squad.getMultisig(vault);
|
||||||
let txKeys = lodash
|
let txKeys = lodash
|
||||||
|
@ -29,7 +31,9 @@ export async function getActiveProposals(
|
||||||
.filter(
|
.filter(
|
||||||
(x: TransactionAccount | null): x is TransactionAccount => x != null
|
(x: TransactionAccount | null): x is TransactionAccount => x != null
|
||||||
)
|
)
|
||||||
.filter((x) => lodash.isEqual(x.status, { active: {} }));
|
.filter((x) =>
|
||||||
|
state === "all" ? true : lodash.isEqual(x.status, { [state]: {} })
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
Loading…
Reference in New Issue