parent
8ef49e6128
commit
31ab162168
|
@ -3748,9 +3748,9 @@
|
||||||
"license": "BSD-3-Clause"
|
"license": "BSD-3-Clause"
|
||||||
},
|
},
|
||||||
"node_modules/@pythnetwork/client": {
|
"node_modules/@pythnetwork/client": {
|
||||||
"version": "2.9.0",
|
"version": "2.10.0",
|
||||||
"resolved": "https://registry.npmjs.org/@pythnetwork/client/-/client-2.9.0.tgz",
|
"resolved": "https://registry.npmjs.org/@pythnetwork/client/-/client-2.10.0.tgz",
|
||||||
"integrity": "sha512-2CyDmTwPWW+JCQgRKUpwMR/31oiLWH6I3GA0h2ZXIcbt/hWxcr5TXyKlWuyi+l+jh73WWh88gq8NXLoIgRcvkA==",
|
"integrity": "sha512-dmj8dAj8K7rhSpaDUIjLGKYs+64kM1LR/V1ht/IShg6Zu0GNJY522+0K0EBcmbjzs3GaHua873DlcvQJlU5iHw==",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"buffer": "^6.0.1"
|
"buffer": "^6.0.1"
|
||||||
},
|
},
|
||||||
|
@ -12759,7 +12759,7 @@
|
||||||
"license": "ISC",
|
"license": "ISC",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@certusone/wormhole-sdk": "^0.9.8",
|
"@certusone/wormhole-sdk": "^0.9.8",
|
||||||
"@pythnetwork/client": "^2.9.0",
|
"@pythnetwork/client": "^2.10.0",
|
||||||
"@solana/buffer-layout": "^4.0.1",
|
"@solana/buffer-layout": "^4.0.1",
|
||||||
"@solana/web3.js": "^1.73.0",
|
"@solana/web3.js": "^1.73.0",
|
||||||
"@sqds/mesh": "^1.0.6",
|
"@sqds/mesh": "^1.0.6",
|
||||||
|
@ -15331,9 +15331,9 @@
|
||||||
"version": "1.1.0"
|
"version": "1.1.0"
|
||||||
},
|
},
|
||||||
"@pythnetwork/client": {
|
"@pythnetwork/client": {
|
||||||
"version": "2.9.0",
|
"version": "2.10.0",
|
||||||
"resolved": "https://registry.npmjs.org/@pythnetwork/client/-/client-2.9.0.tgz",
|
"resolved": "https://registry.npmjs.org/@pythnetwork/client/-/client-2.10.0.tgz",
|
||||||
"integrity": "sha512-2CyDmTwPWW+JCQgRKUpwMR/31oiLWH6I3GA0h2ZXIcbt/hWxcr5TXyKlWuyi+l+jh73WWh88gq8NXLoIgRcvkA==",
|
"integrity": "sha512-dmj8dAj8K7rhSpaDUIjLGKYs+64kM1LR/V1ht/IShg6Zu0GNJY522+0K0EBcmbjzs3GaHua873DlcvQJlU5iHw==",
|
||||||
"requires": {
|
"requires": {
|
||||||
"buffer": "^6.0.1"
|
"buffer": "^6.0.1"
|
||||||
},
|
},
|
||||||
|
@ -21192,7 +21192,7 @@
|
||||||
"version": "file:packages/xc-admin-common",
|
"version": "file:packages/xc-admin-common",
|
||||||
"requires": {
|
"requires": {
|
||||||
"@certusone/wormhole-sdk": "^0.9.8",
|
"@certusone/wormhole-sdk": "^0.9.8",
|
||||||
"@pythnetwork/client": "*",
|
"@pythnetwork/client": "^2.10.0",
|
||||||
"@solana/buffer-layout": "^4.0.1",
|
"@solana/buffer-layout": "^4.0.1",
|
||||||
"@solana/web3.js": "^1.73.0",
|
"@solana/web3.js": "^1.73.0",
|
||||||
"@sqds/mesh": "^1.0.6",
|
"@sqds/mesh": "^1.0.6",
|
||||||
|
|
|
@ -20,7 +20,7 @@
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@certusone/wormhole-sdk": "^0.9.8",
|
"@certusone/wormhole-sdk": "^0.9.8",
|
||||||
"@pythnetwork/client": "^2.9.0",
|
"@pythnetwork/client": "^2.10.0",
|
||||||
"@solana/buffer-layout": "^4.0.1",
|
"@solana/buffer-layout": "^4.0.1",
|
||||||
"@solana/web3.js": "^1.73.0",
|
"@solana/web3.js": "^1.73.0",
|
||||||
"@sqds/mesh": "^1.0.6",
|
"@sqds/mesh": "^1.0.6",
|
||||||
|
|
|
@ -0,0 +1,167 @@
|
||||||
|
import { AnchorProvider, Wallet } from "@project-serum/anchor";
|
||||||
|
import { pythOracleProgram } from "@pythnetwork/client";
|
||||||
|
import {
|
||||||
|
getPythClusterApiUrl,
|
||||||
|
getPythProgramKeyForCluster,
|
||||||
|
PythCluster,
|
||||||
|
} from "@pythnetwork/client/lib/cluster";
|
||||||
|
import { Connection, Keypair, PublicKey } from "@solana/web3.js";
|
||||||
|
import { MultisigInstructionProgram, MultisigParser } from "..";
|
||||||
|
import { PythMultisigInstruction } from "../multisig_transaction/PythMultisigInstruction";
|
||||||
|
|
||||||
|
test("Pyth multisig instruction parse: create price account", (done) => {
|
||||||
|
jest.setTimeout(60000);
|
||||||
|
|
||||||
|
const cluster: PythCluster = "devnet";
|
||||||
|
const pythProgram = pythOracleProgram(
|
||||||
|
getPythProgramKeyForCluster(cluster),
|
||||||
|
new AnchorProvider(
|
||||||
|
new Connection(getPythClusterApiUrl(cluster)),
|
||||||
|
new Wallet(new Keypair()),
|
||||||
|
AnchorProvider.defaultOptions()
|
||||||
|
)
|
||||||
|
);
|
||||||
|
const parser = MultisigParser.fromCluster(cluster);
|
||||||
|
|
||||||
|
pythProgram.methods
|
||||||
|
.addPrice(-8, 1)
|
||||||
|
.accounts({
|
||||||
|
fundingAccount: PublicKey.unique(),
|
||||||
|
productAccount: PublicKey.unique(),
|
||||||
|
priceAccount: PublicKey.unique(),
|
||||||
|
})
|
||||||
|
.instruction()
|
||||||
|
.then((instruction) => {
|
||||||
|
const parsedInstruction = parser.parseInstruction(instruction);
|
||||||
|
|
||||||
|
if (parsedInstruction instanceof PythMultisigInstruction) {
|
||||||
|
expect(parsedInstruction.program).toBe(
|
||||||
|
MultisigInstructionProgram.PythOracle
|
||||||
|
);
|
||||||
|
expect(parsedInstruction.name).toBe("addPrice");
|
||||||
|
expect(
|
||||||
|
parsedInstruction.accounts.named["fundingAccount"].pubkey.equals(
|
||||||
|
instruction.keys[0].pubkey
|
||||||
|
)
|
||||||
|
).toBeTruthy();
|
||||||
|
expect(
|
||||||
|
parsedInstruction.accounts.named["fundingAccount"].isSigner
|
||||||
|
).toBe(instruction.keys[0].isSigner);
|
||||||
|
expect(
|
||||||
|
parsedInstruction.accounts.named["fundingAccount"].isWritable
|
||||||
|
).toBe(instruction.keys[0].isWritable);
|
||||||
|
console.log(parsedInstruction.accounts.named["productAccount"]);
|
||||||
|
expect(
|
||||||
|
parsedInstruction.accounts.named["productAccount"].pubkey.equals(
|
||||||
|
instruction.keys[1].pubkey
|
||||||
|
)
|
||||||
|
).toBeTruthy();
|
||||||
|
expect(
|
||||||
|
parsedInstruction.accounts.named["productAccount"].isSigner
|
||||||
|
).toBe(instruction.keys[1].isSigner);
|
||||||
|
expect(
|
||||||
|
parsedInstruction.accounts.named["productAccount"].isWritable
|
||||||
|
).toBe(instruction.keys[1].isWritable);
|
||||||
|
expect(
|
||||||
|
parsedInstruction.accounts.named["priceAccount"].pubkey.equals(
|
||||||
|
instruction.keys[2].pubkey
|
||||||
|
)
|
||||||
|
).toBeTruthy();
|
||||||
|
expect(parsedInstruction.accounts.named["priceAccount"].isSigner).toBe(
|
||||||
|
instruction.keys[2].isSigner
|
||||||
|
);
|
||||||
|
expect(
|
||||||
|
parsedInstruction.accounts.named["priceAccount"].isWritable
|
||||||
|
).toBe(instruction.keys[2].isWritable);
|
||||||
|
expect(
|
||||||
|
parsedInstruction.accounts.named["permissionsAccount"].pubkey.equals(
|
||||||
|
instruction.keys[3].pubkey
|
||||||
|
)
|
||||||
|
).toBeTruthy();
|
||||||
|
expect(
|
||||||
|
parsedInstruction.accounts.named["permissionsAccount"].isSigner
|
||||||
|
).toBe(instruction.keys[3].isSigner);
|
||||||
|
expect(
|
||||||
|
parsedInstruction.accounts.named["permissionsAccount"].isWritable
|
||||||
|
).toBe(instruction.keys[3].isWritable);
|
||||||
|
expect(parsedInstruction.accounts.remaining.length).toBe(0);
|
||||||
|
|
||||||
|
expect(parsedInstruction.args.expo).toBe(-8);
|
||||||
|
expect(parsedInstruction.args.pType).toBe(1);
|
||||||
|
done();
|
||||||
|
} else {
|
||||||
|
done("Not instance of PythMultisigInstruction");
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
test("Pyth multisig instruction parse: set minimum publishers", (done) => {
|
||||||
|
jest.setTimeout(60000);
|
||||||
|
|
||||||
|
const cluster: PythCluster = "devnet";
|
||||||
|
const pythProgram = pythOracleProgram(
|
||||||
|
getPythProgramKeyForCluster(cluster),
|
||||||
|
new AnchorProvider(
|
||||||
|
new Connection(getPythClusterApiUrl(cluster)),
|
||||||
|
new Wallet(new Keypair()),
|
||||||
|
AnchorProvider.defaultOptions()
|
||||||
|
)
|
||||||
|
);
|
||||||
|
const parser = MultisigParser.fromCluster(cluster);
|
||||||
|
|
||||||
|
pythProgram.methods
|
||||||
|
.setMinPub(25, [0, 0, 0])
|
||||||
|
.accounts({
|
||||||
|
fundingAccount: PublicKey.unique(),
|
||||||
|
priceAccount: PublicKey.unique(),
|
||||||
|
})
|
||||||
|
.instruction()
|
||||||
|
.then((instruction) => {
|
||||||
|
const parsedInstruction = parser.parseInstruction(instruction);
|
||||||
|
|
||||||
|
if (parsedInstruction instanceof PythMultisigInstruction) {
|
||||||
|
expect(parsedInstruction.program).toBe(
|
||||||
|
MultisigInstructionProgram.PythOracle
|
||||||
|
);
|
||||||
|
expect(parsedInstruction.name).toBe("setMinPub");
|
||||||
|
expect(
|
||||||
|
parsedInstruction.accounts.named["fundingAccount"].pubkey.equals(
|
||||||
|
instruction.keys[0].pubkey
|
||||||
|
)
|
||||||
|
).toBeTruthy();
|
||||||
|
expect(
|
||||||
|
parsedInstruction.accounts.named["fundingAccount"].isSigner
|
||||||
|
).toBe(instruction.keys[0].isSigner);
|
||||||
|
expect(
|
||||||
|
parsedInstruction.accounts.named["fundingAccount"].isWritable
|
||||||
|
).toBe(instruction.keys[0].isWritable);
|
||||||
|
expect(
|
||||||
|
parsedInstruction.accounts.named["priceAccount"].pubkey.equals(
|
||||||
|
instruction.keys[1].pubkey
|
||||||
|
)
|
||||||
|
).toBeTruthy();
|
||||||
|
expect(parsedInstruction.accounts.named["priceAccount"].isSigner).toBe(
|
||||||
|
instruction.keys[1].isSigner
|
||||||
|
);
|
||||||
|
expect(
|
||||||
|
parsedInstruction.accounts.named["priceAccount"].isWritable
|
||||||
|
).toBe(instruction.keys[1].isWritable);
|
||||||
|
expect(
|
||||||
|
parsedInstruction.accounts.named["permissionsAccount"].pubkey.equals(
|
||||||
|
instruction.keys[2].pubkey
|
||||||
|
)
|
||||||
|
).toBeTruthy();
|
||||||
|
expect(
|
||||||
|
parsedInstruction.accounts.named["permissionsAccount"].isSigner
|
||||||
|
).toBe(instruction.keys[2].isSigner);
|
||||||
|
expect(
|
||||||
|
parsedInstruction.accounts.named["permissionsAccount"].isWritable
|
||||||
|
).toBe(instruction.keys[2].isWritable);
|
||||||
|
expect(parsedInstruction.accounts.remaining.length).toBe(0);
|
||||||
|
expect(parsedInstruction.args.minPub).toBe(25);
|
||||||
|
done();
|
||||||
|
} else {
|
||||||
|
done("Not instance of PythMultisigInstruction");
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
|
@ -46,9 +46,6 @@ test("Wormhole multisig instruction parse: send message without governance paylo
|
||||||
.instruction()
|
.instruction()
|
||||||
.then((instruction) => {
|
.then((instruction) => {
|
||||||
const parsedInstruction = parser.parseInstruction(instruction);
|
const parsedInstruction = parser.parseInstruction(instruction);
|
||||||
expect(
|
|
||||||
parsedInstruction instanceof WormholeMultisigInstruction
|
|
||||||
).toBeTruthy();
|
|
||||||
if (parsedInstruction instanceof WormholeMultisigInstruction) {
|
if (parsedInstruction instanceof WormholeMultisigInstruction) {
|
||||||
expect(parsedInstruction.program).toBe(
|
expect(parsedInstruction.program).toBe(
|
||||||
MultisigInstructionProgram.WormholeBridge
|
MultisigInstructionProgram.WormholeBridge
|
||||||
|
@ -161,7 +158,7 @@ test("Wormhole multisig instruction parse: send message without governance paylo
|
||||||
expect(parsedInstruction.args.targetChain).toBeUndefined();
|
expect(parsedInstruction.args.targetChain).toBeUndefined();
|
||||||
done();
|
done();
|
||||||
} else {
|
} else {
|
||||||
done("Not instance of WormholeInstruction");
|
done("Not instance of WormholeMultisigInstruction");
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
@ -354,7 +351,7 @@ test("Wormhole multisig instruction parse: send message with governance payload"
|
||||||
done("Not instance of ExecutePostedVaa");
|
done("Not instance of ExecutePostedVaa");
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
done("Not instance of WormholeInstruction");
|
done("Not instance of WormholeMultisigInstruction");
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
|
@ -0,0 +1,44 @@
|
||||||
|
import { MultisigInstruction, MultisigInstructionProgram } from ".";
|
||||||
|
import { AnchorAccounts, resolveAccountNames } from "./anchor";
|
||||||
|
import { pythIdl, pythOracleCoder } from "@pythnetwork/client";
|
||||||
|
import { TransactionInstruction } from "@solana/web3.js";
|
||||||
|
import { Idl } from "@coral-xyz/anchor";
|
||||||
|
|
||||||
|
export class PythMultisigInstruction implements MultisigInstruction {
|
||||||
|
readonly program = MultisigInstructionProgram.PythOracle;
|
||||||
|
readonly name: string;
|
||||||
|
readonly args: { [key: string]: any };
|
||||||
|
readonly accounts: AnchorAccounts;
|
||||||
|
|
||||||
|
constructor(
|
||||||
|
name: string,
|
||||||
|
args: { [key: string]: any },
|
||||||
|
accounts: AnchorAccounts
|
||||||
|
) {
|
||||||
|
this.name = name;
|
||||||
|
this.args = args;
|
||||||
|
this.accounts = accounts;
|
||||||
|
}
|
||||||
|
|
||||||
|
static fromTransactionInstruction(
|
||||||
|
instruction: TransactionInstruction
|
||||||
|
): PythMultisigInstruction {
|
||||||
|
const pythInstructionCoder = pythOracleCoder().instruction;
|
||||||
|
|
||||||
|
const deserializedData = pythInstructionCoder.decode(instruction.data);
|
||||||
|
|
||||||
|
if (deserializedData) {
|
||||||
|
return new PythMultisigInstruction(
|
||||||
|
deserializedData.name,
|
||||||
|
deserializedData.data,
|
||||||
|
resolveAccountNames(pythIdl as Idl, deserializedData.name, instruction)
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
return new PythMultisigInstruction(
|
||||||
|
"Unrecognized instruction",
|
||||||
|
{},
|
||||||
|
{ named: {}, remaining: instruction.keys }
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -4,6 +4,7 @@ import {
|
||||||
} from "@pythnetwork/client/lib/cluster";
|
} from "@pythnetwork/client/lib/cluster";
|
||||||
import { PublicKey, TransactionInstruction } from "@solana/web3.js";
|
import { PublicKey, TransactionInstruction } from "@solana/web3.js";
|
||||||
import { WORMHOLE_ADDRESS } from "../wormhole";
|
import { WORMHOLE_ADDRESS } from "../wormhole";
|
||||||
|
import { PythMultisigInstruction } from "./PythMultisigInstruction";
|
||||||
import { WormholeMultisigInstruction } from "./WormholeMultisigInstruction";
|
import { WormholeMultisigInstruction } from "./WormholeMultisigInstruction";
|
||||||
|
|
||||||
export enum MultisigInstructionProgram {
|
export enum MultisigInstructionProgram {
|
||||||
|
@ -30,11 +31,6 @@ export class UnrecognizedProgram implements MultisigInstruction {
|
||||||
return new UnrecognizedProgram(instruction);
|
return new UnrecognizedProgram(instruction);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export class PythMultisigInstruction implements MultisigInstruction {
|
|
||||||
readonly program = MultisigInstructionProgram.PythOracle;
|
|
||||||
}
|
|
||||||
|
|
||||||
export class MultisigParser {
|
export class MultisigParser {
|
||||||
readonly pythOracleAddress: PublicKey;
|
readonly pythOracleAddress: PublicKey;
|
||||||
readonly wormholeBridgeAddress: PublicKey | undefined;
|
readonly wormholeBridgeAddress: PublicKey | undefined;
|
||||||
|
@ -61,6 +57,8 @@ export class MultisigParser {
|
||||||
return WormholeMultisigInstruction.fromTransactionInstruction(
|
return WormholeMultisigInstruction.fromTransactionInstruction(
|
||||||
instruction
|
instruction
|
||||||
);
|
);
|
||||||
|
} else if (instruction.programId.equals(this.pythOracleAddress)) {
|
||||||
|
return PythMultisigInstruction.fromTransactionInstruction(instruction);
|
||||||
} else {
|
} else {
|
||||||
return UnrecognizedProgram.fromTransactionInstruction(instruction);
|
return UnrecognizedProgram.fromTransactionInstruction(instruction);
|
||||||
}
|
}
|
||||||
|
|
|
@ -6,5 +6,6 @@ export const WORMHOLE_ADDRESS: Record<PythCluster, PublicKey | undefined> = {
|
||||||
pythtest: new PublicKey("EUrRARh92Cdc54xrDn6qzaqjA77NRrCcfbr8kPwoTL4z"),
|
pythtest: new PublicKey("EUrRARh92Cdc54xrDn6qzaqjA77NRrCcfbr8kPwoTL4z"),
|
||||||
devnet: new PublicKey("3u8hJUVTA4jH1wYAyUur7FFZVQ8H635K3tSHHF4ssjQ5"),
|
devnet: new PublicKey("3u8hJUVTA4jH1wYAyUur7FFZVQ8H635K3tSHHF4ssjQ5"),
|
||||||
pythnet: new PublicKey("H3fxXJ86ADW2PNuDDmZJg6mzTtPxkYCpNuQUTgmJ7AjU"),
|
pythnet: new PublicKey("H3fxXJ86ADW2PNuDDmZJg6mzTtPxkYCpNuQUTgmJ7AjU"),
|
||||||
|
localnet: new PublicKey("gMYYig2utAxVoXnM9UhtTWrt8e7x2SVBZqsWZJeT5Gw"),
|
||||||
testnet: undefined,
|
testnet: undefined,
|
||||||
};
|
};
|
||||||
|
|
Loading…
Reference in New Issue