[contract_manager] Support for entropy and evm executor (#1251)
* Add EvmExecute structures for governance * Add ExecuteAction file * uint 256 (#1250) * Add in value field in ExecuteAction * Add value arg in contract manager * add tests for evm execute (#1252) * add tests for evm execute * add tests for buffer layout * remove unneccessary test * accept admin and ownership payload * rename to add entropy * update comment * address comments * minor rename --------- Co-authored-by: Amin Moghaddam <amin@pyth.network>
This commit is contained in:
parent
e1db4aad65
commit
f3ad917c6b
|
@ -0,0 +1,86 @@
|
|||
import yargs from "yargs";
|
||||
import { hideBin } from "yargs/helpers";
|
||||
import { DefaultStore, EvmChain, loadHotWallet } from "../src";
|
||||
|
||||
const parser = yargs(hideBin(process.argv))
|
||||
.usage(
|
||||
"Creates governance proposal to accept pending admin or ownership transfer for Pyth entropy contracts.\n" +
|
||||
"Usage: $0 --chain <chain_1> --chain <chain_2> --ops-key-path <ops_key_path>"
|
||||
)
|
||||
.options({
|
||||
testnet: {
|
||||
type: "boolean",
|
||||
default: false,
|
||||
desc: "Accept for testnet contracts instead of mainnet",
|
||||
},
|
||||
"all-chains": {
|
||||
type: "boolean",
|
||||
default: false,
|
||||
desc: "Accept for contract on all chains. Use with --testnet flag to accept for all testnet contracts",
|
||||
},
|
||||
chain: {
|
||||
type: "array",
|
||||
string: true,
|
||||
desc: "Accept for contract on given chains",
|
||||
},
|
||||
"ops-key-path": {
|
||||
type: "string",
|
||||
demandOption: true,
|
||||
desc: "Path to the private key of the proposer to use for the operations multisig governance proposal",
|
||||
},
|
||||
});
|
||||
|
||||
async function main() {
|
||||
const argv = await parser.argv;
|
||||
const selectedChains: EvmChain[] = [];
|
||||
|
||||
if (argv.allChains && argv.chain)
|
||||
throw new Error("Cannot use both --all-chains and --chain");
|
||||
if (!argv.allChains && !argv.chain)
|
||||
throw new Error("Must use either --all-chains or --chain");
|
||||
for (const chain of Object.values(DefaultStore.chains)) {
|
||||
if (!(chain instanceof EvmChain)) continue;
|
||||
if (
|
||||
(argv.allChains && chain.isMainnet() !== argv.testnet) ||
|
||||
argv.chain?.includes(chain.getId())
|
||||
)
|
||||
selectedChains.push(chain);
|
||||
}
|
||||
if (argv.chain && selectedChains.length !== argv.chain.length)
|
||||
throw new Error(
|
||||
`Some chains were not found ${selectedChains
|
||||
.map((chain) => chain.getId())
|
||||
.toString()}`
|
||||
);
|
||||
for (const chain of selectedChains) {
|
||||
if (chain.isMainnet() != selectedChains[0].isMainnet())
|
||||
throw new Error("All chains must be either mainnet or testnet");
|
||||
}
|
||||
|
||||
const vault =
|
||||
DefaultStore.vaults[
|
||||
"mainnet-beta_FVQyHcooAtThJ83XFrNnv74BcinbRH3bRmfFamAHBfuj"
|
||||
];
|
||||
|
||||
const payloads: Buffer[] = [];
|
||||
for (const contract of Object.values(DefaultStore.entropy_contracts)) {
|
||||
if (selectedChains.includes(contract.chain)) {
|
||||
console.log("Creating payload for chain: ", contract.chain.getId());
|
||||
const pendingOwner = await contract.getPendingOwner();
|
||||
const adminPayload = contract.generateAcceptAdminPayload(pendingOwner);
|
||||
const ownerPayload =
|
||||
contract.generateAcceptOwnershipPayload(pendingOwner);
|
||||
|
||||
payloads.push(adminPayload, ownerPayload);
|
||||
}
|
||||
}
|
||||
|
||||
console.log("Using vault at for proposal", vault.getId());
|
||||
const wallet = await loadHotWallet(argv["ops-key-path"]);
|
||||
console.log("Using wallet ", wallet.publicKey.toBase58());
|
||||
await vault.connect(wallet);
|
||||
const proposal = await vault.proposeWormholeMessage(payloads);
|
||||
console.log("Proposal address", proposal.address.toBase58());
|
||||
}
|
||||
|
||||
main();
|
|
@ -3,11 +3,54 @@ import PythInterfaceAbi from "@pythnetwork/pyth-sdk-solidity/abis/IPyth.json";
|
|||
import EntropyAbi from "@pythnetwork/entropy-sdk-solidity/abis/IEntropy.json";
|
||||
import { PriceFeedContract, PrivateKey, Storable } from "../base";
|
||||
import { Chain, EvmChain } from "../chains";
|
||||
import { DataSource } from "xc_admin_common";
|
||||
import { DataSource, EvmExecute } from "xc_admin_common";
|
||||
import { WormholeContract } from "./wormhole";
|
||||
|
||||
// Just to make sure tx gas limit is enough
|
||||
const GAS_ESTIMATE_MULTIPLIER = 2;
|
||||
const EXTENDED_ENTROPY_ABI = [
|
||||
{
|
||||
inputs: [],
|
||||
name: "acceptOwnership",
|
||||
outputs: [],
|
||||
stateMutability: "nonpayable",
|
||||
type: "function",
|
||||
},
|
||||
{
|
||||
inputs: [],
|
||||
name: "acceptAdmin",
|
||||
outputs: [],
|
||||
stateMutability: "nonpayable",
|
||||
type: "function",
|
||||
},
|
||||
{
|
||||
inputs: [],
|
||||
name: "owner",
|
||||
outputs: [
|
||||
{
|
||||
internalType: "address",
|
||||
name: "",
|
||||
type: "address",
|
||||
},
|
||||
],
|
||||
stateMutability: "view",
|
||||
type: "function",
|
||||
},
|
||||
{
|
||||
inputs: [],
|
||||
name: "pendingOwner",
|
||||
outputs: [
|
||||
{
|
||||
internalType: "address",
|
||||
name: "",
|
||||
type: "address",
|
||||
},
|
||||
],
|
||||
stateMutability: "view",
|
||||
type: "function",
|
||||
},
|
||||
...EntropyAbi,
|
||||
] as any; // eslint-disable-line @typescript-eslint/no-explicit-any
|
||||
const EXTENDED_PYTH_ABI = [
|
||||
{
|
||||
inputs: [],
|
||||
|
@ -321,6 +364,42 @@ export class EvmEntropyContract extends Storable {
|
|||
return new EvmEntropyContract(chain, parsed.address);
|
||||
}
|
||||
|
||||
// Generate a payload for the given executor address and calldata.
|
||||
// `executor` and `calldata` should be hex strings.
|
||||
generateExecutorPayload(executor: string, calldata: string) {
|
||||
return new EvmExecute(
|
||||
this.chain.wormholeChainName,
|
||||
executor.replace("0x", ""),
|
||||
this.address.replace("0x", ""),
|
||||
0n,
|
||||
Buffer.from(calldata.replace("0x", ""), "hex")
|
||||
).encode();
|
||||
}
|
||||
|
||||
// Generates a payload for the newAdmin to call acceptAdmin on the entropy contracts
|
||||
generateAcceptAdminPayload(newAdmin: string): Buffer {
|
||||
const contract = this.getContract();
|
||||
const data = contract.methods.acceptAdmin().encodeABI();
|
||||
return this.generateExecutorPayload(newAdmin, data);
|
||||
}
|
||||
|
||||
// Generates a payload for newOwner to call acceptOwnership on the entropy contracts
|
||||
generateAcceptOwnershipPayload(newOwner: string): Buffer {
|
||||
const contract = this.getContract();
|
||||
const data = contract.methods.acceptOwnership().encodeABI();
|
||||
return this.generateExecutorPayload(newOwner, data);
|
||||
}
|
||||
|
||||
getOwner(): string {
|
||||
const contract = this.getContract();
|
||||
return contract.methods.owner().call();
|
||||
}
|
||||
|
||||
getPendingOwner(): string {
|
||||
const contract = this.getContract();
|
||||
return contract.methods.pendingOwner().call();
|
||||
}
|
||||
|
||||
toJson() {
|
||||
return {
|
||||
chain: this.chain.getId(),
|
||||
|
@ -331,7 +410,7 @@ export class EvmEntropyContract extends Storable {
|
|||
|
||||
getContract() {
|
||||
const web3 = new Web3(this.chain.getRpcUrl());
|
||||
return new web3.eth.Contract(EntropyAbi as any, this.address); // eslint-disable-line @typescript-eslint/no-explicit-any
|
||||
return new web3.eth.Contract(EXTENDED_ENTROPY_ABI, this.address);
|
||||
}
|
||||
|
||||
async getDefaultProvider(): Promise<string> {
|
||||
|
|
|
@ -428,3 +428,18 @@
|
|||
rpcUrl: https://rpc.ankr.com/filecoin
|
||||
networkId: 314
|
||||
type: EvmChain
|
||||
- id: lightlink_pegasus_testnet
|
||||
mainnet: false
|
||||
rpcUrl: https://replicator.pegasus.lightlink.io/rpc/v1
|
||||
networkId: 1891
|
||||
type: EvmChain
|
||||
- id: sei_evm_devnet
|
||||
mainnet: false
|
||||
rpcUrl: https://evm-devnet.seinetwork.io
|
||||
networkId: 713715
|
||||
type: EvmChain
|
||||
- id: fantom_sonic_testnet
|
||||
mainnet: false
|
||||
rpcUrl: https://rpc.sonic.fantom.network/
|
||||
networkId: 64165
|
||||
type: EvmChain
|
||||
|
|
|
@ -1,3 +1,24 @@
|
|||
- chain: lightlink_pegasus_testnet
|
||||
address: "0x8250f4aF4B972684F7b336503E2D6dFeDeB1487a"
|
||||
type: EvmEntropyContract
|
||||
- chain: chiliz_spicy
|
||||
address: "0xD458261E832415CFd3BAE5E416FdF3230ce6F134"
|
||||
type: EvmEntropyContract
|
||||
- chain: conflux_espace_testnet
|
||||
address: "0xdF21D137Aadc95588205586636710ca2890538d5"
|
||||
type: EvmEntropyContract
|
||||
- chain: mode_testnet
|
||||
address: "0x98046Bd286715D3B0BC227Dd7a956b83D8978603"
|
||||
type: EvmEntropyContract
|
||||
- chain: arbitrum_sepolia
|
||||
address: "0x549Ebba8036Ab746611B4fFA1423eb0A4Df61440"
|
||||
type: EvmEntropyContract
|
||||
- chain: base_goerli
|
||||
address: "0x4374e5a8b9C22271E9EB878A2AA31DE97DF15DAF"
|
||||
type: EvmPriceFeedContract
|
||||
type: EvmEntropyContract
|
||||
- chain: fantom_sonic_testnet
|
||||
address: "0xb27e5ca259702f209a29225d0eDdC131039C9933"
|
||||
type: EvmEntropyContract
|
||||
- chain: blast_s2_testnet
|
||||
address: "0x98046Bd286715D3B0BC227Dd7a956b83D8978603"
|
||||
type: EvmEntropyContract
|
||||
|
|
|
@ -26,6 +26,7 @@
|
|||
"@solana/buffer-layout": "^4.0.1",
|
||||
"@solana/web3.js": "^1.73.0",
|
||||
"@sqds/mesh": "^1.0.6",
|
||||
"bigint-buffer": "^1.1.5",
|
||||
"ethers": "^5.7.2",
|
||||
"lodash": "^4.17.21",
|
||||
"typescript": "^4.9.4"
|
||||
|
@ -34,9 +35,9 @@
|
|||
"@types/bn.js": "^5.1.1",
|
||||
"@types/jest": "^29.2.5",
|
||||
"@types/lodash": "^4.14.191",
|
||||
"fast-check": "^3.10.0",
|
||||
"jest": "^29.3.1",
|
||||
"prettier": "^2.8.1",
|
||||
"ts-jest": "^29.0.3",
|
||||
"fast-check": "^3.10.0"
|
||||
"ts-jest": "^29.0.3"
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,20 @@
|
|||
import fc from "fast-check";
|
||||
import { u64be } from "../governance_payload/BufferLayoutExt";
|
||||
|
||||
test("Buffer layout extension fc tests", (done) => {
|
||||
const u64 = u64be();
|
||||
fc.assert(
|
||||
fc.property(fc.bigUintN(64), (bi) => {
|
||||
let encodedUint8Array = new Uint8Array(8);
|
||||
u64.encode(bi, encodedUint8Array);
|
||||
|
||||
let buffer = Buffer.alloc(8);
|
||||
buffer.writeBigUInt64BE(bi);
|
||||
|
||||
const decodedBI = u64.decode(buffer);
|
||||
return buffer.equals(encodedUint8Array) && bi === decodedBI;
|
||||
})
|
||||
);
|
||||
|
||||
done();
|
||||
});
|
|
@ -10,6 +10,8 @@ import {
|
|||
PythGovernanceAction,
|
||||
decodeGovernancePayload,
|
||||
EvmSetWormholeAddress,
|
||||
EvmExecutorAction,
|
||||
EvmExecute,
|
||||
} from "..";
|
||||
import * as fc from "fast-check";
|
||||
import { ChainName, CHAINS } from "../chains";
|
||||
|
@ -66,6 +68,14 @@ test("GovernancePayload ser/de", (done) => {
|
|||
expect(governanceHeader?.targetChainId).toBe("solana");
|
||||
expect(governanceHeader?.action).toBe("SetFee");
|
||||
|
||||
// Valid header 3
|
||||
expectedGovernanceHeader = new PythGovernanceHeader("solana", "Execute");
|
||||
buffer = expectedGovernanceHeader.encode();
|
||||
expect(buffer.equals(Buffer.from([80, 84, 71, 77, 2, 0, 0, 1]))).toBeTruthy();
|
||||
governanceHeader = PythGovernanceHeader.decode(buffer);
|
||||
expect(governanceHeader?.targetChainId).toBe("solana");
|
||||
expect(governanceHeader?.action).toBe("Execute");
|
||||
|
||||
// Wrong magic number
|
||||
expect(
|
||||
PythGovernanceHeader.decode(
|
||||
|
@ -157,6 +167,7 @@ function governanceHeaderArb(): Arbitrary<PythGovernanceHeader> {
|
|||
const actions = [
|
||||
...Object.keys(ExecutorAction),
|
||||
...Object.keys(TargetAction),
|
||||
...Object.keys(EvmExecutorAction),
|
||||
] as ActionName[];
|
||||
const actionArb = fc.constantFrom(...actions);
|
||||
const targetChainIdArb = fc.constantFrom(
|
||||
|
@ -260,6 +271,24 @@ function governanceActionArb(): Arbitrary<PythGovernanceAction> {
|
|||
return hexBytesArb({ minLength: 20, maxLength: 20 }).map((address) => {
|
||||
return new EvmSetWormholeAddress(header.targetChainId, address);
|
||||
});
|
||||
} else if (header.action === "Execute") {
|
||||
return fc
|
||||
.record({
|
||||
executerAddress: hexBytesArb({ minLength: 20, maxLength: 20 }),
|
||||
callAddress: hexBytesArb({ minLength: 20, maxLength: 20 }),
|
||||
value: fc.bigUintN(256),
|
||||
callData: bufferArb(),
|
||||
})
|
||||
.map(
|
||||
({ executerAddress, callAddress, value, callData }) =>
|
||||
new EvmExecute(
|
||||
header.targetChainId,
|
||||
executerAddress,
|
||||
callAddress,
|
||||
value,
|
||||
callData
|
||||
)
|
||||
);
|
||||
} else {
|
||||
throw new Error("Unsupported action type");
|
||||
}
|
||||
|
|
|
@ -1,25 +1,20 @@
|
|||
import { Layout } from "@solana/buffer-layout";
|
||||
import { toBigIntBE, toBufferBE } from "bigint-buffer";
|
||||
|
||||
export class UInt64BE extends Layout<bigint> {
|
||||
export class UIntBE extends Layout<bigint> {
|
||||
// span is the number of bytes to read
|
||||
constructor(span: number, property?: string) {
|
||||
super(span, property);
|
||||
}
|
||||
|
||||
// Note: we can not use read/writeBigUInt64BE because it is not supported in the browsers
|
||||
override decode(b: Uint8Array, offset?: number): bigint {
|
||||
let o = offset ?? 0;
|
||||
const buffer = Buffer.from(b.slice(o, o + this.span));
|
||||
const hi32 = buffer.readUInt32BE();
|
||||
const lo32 = buffer.readUInt32BE(4);
|
||||
return BigInt(lo32) + (BigInt(hi32) << BigInt(32));
|
||||
return toBigIntBE(buffer);
|
||||
}
|
||||
|
||||
override encode(src: bigint, b: Uint8Array, offset?: number): number {
|
||||
const buffer = Buffer.alloc(this.span);
|
||||
const hi32 = Number(src >> BigInt(32));
|
||||
const lo32 = Number(src & BigInt(0xffffffff));
|
||||
buffer.writeUInt32BE(hi32, 0);
|
||||
buffer.writeUInt32BE(lo32, 4);
|
||||
const buffer = toBufferBE(src, this.span);
|
||||
b.set(buffer, offset);
|
||||
return this.span;
|
||||
}
|
||||
|
@ -45,8 +40,13 @@ export class HexBytes extends Layout<string> {
|
|||
}
|
||||
|
||||
/** A big-endian u64, returned as a bigint. */
|
||||
export function u64be(property?: string | undefined): UInt64BE {
|
||||
return new UInt64BE(8, property);
|
||||
export function u64be(property?: string | undefined): UIntBE {
|
||||
return new UIntBE(8, property);
|
||||
}
|
||||
|
||||
/** A big-endian u256, returned as a bigint. */
|
||||
export function u256be(property?: string | undefined): UIntBE {
|
||||
return new UIntBE(32, property);
|
||||
}
|
||||
|
||||
/** An array of numBytes bytes, returned as a hexadecimal string. */
|
||||
|
|
|
@ -0,0 +1,73 @@
|
|||
import { PythGovernanceActionImpl } from "./PythGovernanceAction";
|
||||
import * as BufferLayout from "@solana/buffer-layout";
|
||||
import * as BufferLayoutExt from "./BufferLayoutExt";
|
||||
import { ChainName } from "../chains";
|
||||
|
||||
/** Executes an action from the executor contract via the specified executorAddress, callAddress, value, and calldata */
|
||||
export class EvmExecute extends PythGovernanceActionImpl {
|
||||
static layout: BufferLayout.Structure<
|
||||
Readonly<{
|
||||
executorAddress: string;
|
||||
callAddress: string;
|
||||
value: bigint;
|
||||
calldata: Uint8Array;
|
||||
}>
|
||||
> = BufferLayout.struct([
|
||||
BufferLayoutExt.hexBytes(20, "executorAddress"),
|
||||
BufferLayoutExt.hexBytes(20, "callAddress"),
|
||||
BufferLayoutExt.u256be("value"),
|
||||
BufferLayout.blob(new BufferLayout.GreedyCount(), "calldata"),
|
||||
]);
|
||||
|
||||
constructor(
|
||||
targetChainId: ChainName,
|
||||
readonly executorAddress: string,
|
||||
readonly callAddress: string,
|
||||
readonly value: bigint,
|
||||
readonly calldata: Buffer
|
||||
) {
|
||||
super(targetChainId, "Execute");
|
||||
}
|
||||
|
||||
static decode(data: Buffer): EvmExecute | undefined {
|
||||
const decoded = PythGovernanceActionImpl.decodeWithPayload(
|
||||
data,
|
||||
"Execute",
|
||||
this.layout
|
||||
);
|
||||
if (!decoded) return undefined;
|
||||
|
||||
return new EvmExecute(
|
||||
decoded[0].targetChainId,
|
||||
decoded[1].executorAddress,
|
||||
decoded[1].callAddress,
|
||||
decoded[1].value,
|
||||
Buffer.from(decoded[1].calldata)
|
||||
);
|
||||
}
|
||||
|
||||
encode(): Buffer {
|
||||
// encodeWithPayload creates a buffer using layout.span but EvmExecute.layout span is -1
|
||||
// because the calldata length is unknown. So we create a layout with a known calldata length
|
||||
// and use that for encoding
|
||||
const layout_with_known_span: BufferLayout.Structure<
|
||||
Readonly<{
|
||||
executorAddress: string;
|
||||
callAddress: string;
|
||||
value: bigint;
|
||||
calldata: Uint8Array;
|
||||
}>
|
||||
> = BufferLayout.struct([
|
||||
BufferLayoutExt.hexBytes(20, "executorAddress"),
|
||||
BufferLayoutExt.hexBytes(20, "callAddress"),
|
||||
BufferLayoutExt.u256be("value"),
|
||||
BufferLayout.blob(this.calldata.length, "calldata"),
|
||||
]);
|
||||
return super.encodeWithPayload(layout_with_known_span, {
|
||||
executorAddress: this.executorAddress,
|
||||
callAddress: this.callAddress,
|
||||
value: this.value,
|
||||
calldata: this.calldata,
|
||||
});
|
||||
}
|
||||
}
|
|
@ -17,6 +17,10 @@ export const TargetAction = {
|
|||
SetWormholeAddress: 6,
|
||||
} as const;
|
||||
|
||||
export const EvmExecutorAction = {
|
||||
Execute: 0,
|
||||
} as const;
|
||||
|
||||
/** Helper to get the ActionName from a (moduleId, actionId) tuple*/
|
||||
export function toActionName(
|
||||
deserialized: Readonly<{ moduleId: number; actionId: number }>
|
||||
|
@ -40,13 +44,19 @@ export function toActionName(
|
|||
case 6:
|
||||
return "SetWormholeAddress";
|
||||
}
|
||||
} else if (
|
||||
deserialized.moduleId == MODULE_EVM_EXECUTOR &&
|
||||
deserialized.actionId == 0
|
||||
) {
|
||||
return "Execute";
|
||||
}
|
||||
return undefined;
|
||||
}
|
||||
|
||||
export declare type ActionName =
|
||||
| keyof typeof ExecutorAction
|
||||
| keyof typeof TargetAction;
|
||||
| keyof typeof TargetAction
|
||||
| keyof typeof EvmExecutorAction;
|
||||
|
||||
/** Governance header that should be in every Pyth crosschain governance message*/
|
||||
export class PythGovernanceHeader {
|
||||
|
@ -109,9 +119,12 @@ export class PythGovernanceHeader {
|
|||
if (this.action in ExecutorAction) {
|
||||
module = MODULE_EXECUTOR;
|
||||
action = ExecutorAction[this.action as keyof typeof ExecutorAction];
|
||||
} else {
|
||||
} else if (this.action in TargetAction) {
|
||||
module = MODULE_TARGET;
|
||||
action = TargetAction[this.action as keyof typeof TargetAction];
|
||||
} else {
|
||||
module = MODULE_EVM_EXECUTOR;
|
||||
action = EvmExecutorAction[this.action as keyof typeof EvmExecutorAction];
|
||||
}
|
||||
if (toChainId(this.targetChainId) === undefined)
|
||||
throw new Error(`Invalid chain id ${this.targetChainId}`);
|
||||
|
@ -131,10 +144,12 @@ export class PythGovernanceHeader {
|
|||
export const MAGIC_NUMBER = 0x4d475450;
|
||||
export const MODULE_EXECUTOR = 0;
|
||||
export const MODULE_TARGET = 1;
|
||||
export const MODULES = [MODULE_EXECUTOR, MODULE_TARGET];
|
||||
export const MODULE_EVM_EXECUTOR = 2;
|
||||
export const MODULES = [MODULE_EXECUTOR, MODULE_TARGET, MODULE_EVM_EXECUTOR];
|
||||
|
||||
export interface PythGovernanceAction {
|
||||
readonly targetChainId: ChainName;
|
||||
|
||||
encode(): Buffer;
|
||||
}
|
||||
|
||||
|
|
|
@ -16,6 +16,7 @@ import { SetDataSources } from "./SetDataSources";
|
|||
import { SetValidPeriod } from "./SetValidPeriod";
|
||||
import { SetFee } from "./SetFee";
|
||||
import { EvmSetWormholeAddress } from "./SetWormholeAddress";
|
||||
import { EvmExecute } from "./ExecuteAction";
|
||||
|
||||
/** Decode a governance payload */
|
||||
export function decodeGovernancePayload(
|
||||
|
@ -53,6 +54,8 @@ export function decodeGovernancePayload(
|
|||
return RequestGovernanceDataSourceTransfer.decode(data);
|
||||
case "SetWormholeAddress":
|
||||
return EvmSetWormholeAddress.decode(data);
|
||||
case "Execute":
|
||||
return EvmExecute.decode(data);
|
||||
default:
|
||||
return undefined;
|
||||
}
|
||||
|
@ -67,3 +70,4 @@ export * from "./SetDataSources";
|
|||
export * from "./SetValidPeriod";
|
||||
export * from "./SetFee";
|
||||
export * from "./SetWormholeAddress";
|
||||
export * from "./ExecuteAction";
|
||||
|
|
|
@ -1918,6 +1918,7 @@
|
|||
"@solana/buffer-layout": "^4.0.1",
|
||||
"@solana/web3.js": "^1.73.0",
|
||||
"@sqds/mesh": "^1.0.6",
|
||||
"bigint-buffer": "^1.1.5",
|
||||
"ethers": "^5.7.2",
|
||||
"lodash": "^4.17.21",
|
||||
"typescript": "^4.9.4"
|
||||
|
@ -104649,6 +104650,7 @@
|
|||
"@types/bn.js": "^5.1.1",
|
||||
"@types/jest": "^29.2.5",
|
||||
"@types/lodash": "^4.14.191",
|
||||
"bigint-buffer": "*",
|
||||
"ethers": "^5.7.2",
|
||||
"fast-check": "^3.10.0",
|
||||
"jest": "^29.3.1",
|
||||
|
|
Loading…
Reference in New Issue