Skip to content

Commit

Permalink
solana: add docker for generating governance instructions
Browse files Browse the repository at this point in the history
  • Loading branch information
kcsongor committed Apr 23, 2024
1 parent bd495e2 commit c65563d
Show file tree
Hide file tree
Showing 5 changed files with 149 additions and 8 deletions.
17 changes: 17 additions & 0 deletions solana/Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -42,3 +42,20 @@ COPY solana/Makefile Makefile
COPY solana/scripts scripts

RUN make target/idl/example_native_token_transfers.json

FROM node:lts-alpine3.19@sha256:ec0c413b1d84f3f7f67ec986ba885930c57b5318d2eb3abc6960ee05d4f2eb28 as governance

WORKDIR /app

# RUN apk add python3
RUN apk add make python3 gcc g++

COPY tsconfig.json tsconfig.json
COPY package.json package.json
COPY package-lock.json package-lock.json
RUN npm ci

COPY idl idl
COPY ts ts

ENTRYPOINT [ "npx", "ts-node", "-T" ]
3 changes: 2 additions & 1 deletion solana/ts/sdk/index.ts
Original file line number Diff line number Diff line change
@@ -1,2 +1,3 @@
import "./side-effects";
export * from "./ntt";
export * from "./quoter";
export * from "./quoter";
64 changes: 64 additions & 0 deletions solana/ts/sdk/ntt.ts
Original file line number Diff line number Diff line change
Expand Up @@ -105,6 +105,10 @@ export class NTT {
return this.derivePda('config')
}

upgradeLockAccountAddress(): PublicKey {
return this.derivePda('upgrade_lock')
}

outboxRateLimitAccountAddress(): PublicKey {
return this.derivePda('outbox_rate_limit')
}
Expand Down Expand Up @@ -307,6 +311,66 @@ export class NTT {
return outboxItem.publicKey
}

async transferOwnership(args: {
payer: Keypair
owner: Keypair,
newOwner: PublicKey
}) {
const ix = await this.createTransferOwnershipInstruction({
owner: args.owner.publicKey,
newOwner: args.newOwner
})
return await this.sendAndConfirmTransaction(
new Transaction().add(ix),
[args.payer, args.owner]
)
}

async createTransferOwnershipInstruction(args: {
owner: PublicKey;
newOwner: PublicKey;
}) {
return this.program.methods
.transferOwnership()
.accountsStrict({
config: this.configAccountAddress(),
owner: args.owner,
newOwner: args.newOwner,
upgradeLock: this.upgradeLockAccountAddress(),
programData: programDataAddress(this.program.programId),
bpfLoaderUpgradeableProgram: BPF_LOADER_UPGRADEABLE_PROGRAM_ID,
})
.instruction();
}

async claimOwnership(args: {
payer: Keypair
owner: Keypair
}) {
const ix = await this.createClaimOwnershipInstruction({
newOwner: args.owner.publicKey
})
return await this.sendAndConfirmTransaction(
new Transaction().add(ix),
[args.payer, args.owner]
)
}

async createClaimOwnershipInstruction(args: {
newOwner: PublicKey;
}) {
return this.program.methods
.claimOwnership()
.accountsStrict({
config: this.configAccountAddress(),
upgradeLock: this.upgradeLockAccountAddress(),
newOwner: args.newOwner,
programData: programDataAddress(this.program.programId),
bpfLoaderUpgradeableProgram: BPF_LOADER_UPGRADEABLE_PROGRAM_ID,
})
.instruction();
}

/**
* Like `sendAndConfirmTransaction` but parses the anchor error code.
*/
Expand Down
26 changes: 26 additions & 0 deletions solana/ts/sdk/side-effects.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
// <sigh>
// when the native secp256k1 is missing, the eccrypto library decides TO PRINT A MESSAGE TO STDOUT:
// https://github.com/bitchan/eccrypto/blob/a4f4a5f85ef5aa1776dfa1b7801cad808264a19c/index.js#L23
//
// do you use a CLI tool that depends on that library and try to pipe the output
// of the tool into another? tough luck
//
// for lack of a better way to stop this, we patch the console.info function to
// drop that particular message...
// </sigh>
const info = console.info;
console.info = function (x: string) {
if (x !== "secp256k1 unavailable, reverting to browser version") {
info(x);
}
};

const warn = console.warn;
console.warn = function (x: string) {
if (
x !==
"bigint: Failed to load bindings, pure JS will be used (try npm run rebuild?)"
) {
warn(x);
}
};
47 changes: 40 additions & 7 deletions solana/ts/sdk/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import {
encoding,
} from "@wormhole-foundation/sdk-base";

import { PublicKey, PublicKeyInitData } from "@solana/web3.js";
import { PublicKey, PublicKeyInitData, TransactionInstruction } from "@solana/web3.js";
import { BN } from "@coral-xyz/anchor";

const CHAIN_ID_BYTE_SIZE = 2;
Expand Down Expand Up @@ -39,13 +39,13 @@ export const U64 = {
MAX: new BN((2n**64n - 1n).toString()),
to: (amount: number, unit: number) => {
const ret = new BN(Math.round(amount * unit));

if (ret.isNeg())
throw new Error("Value negative");

if (ret.bitLength() > 64)
throw new Error("Value too large");
throw new Error("Value too large");

return ret;
},
from: (amount: BN, unit: number) => amount.toNumber() / unit,
Expand All @@ -57,8 +57,41 @@ export function derivePda(
programId: PublicKeyInitData
) {
const toBytes = (s: string | Uint8Array) => typeof s === "string" ? encoding.bytes.encode(s) : s;
return PublicKey.findProgramAddressSync(
return PublicKey.findProgramAddressSync(
Array.isArray(seeds) ? seeds.map(toBytes) : [toBytes(seeds as Seed)],
new PublicKey(programId),
)[0];
}
}

// governance utils

export function serializeInstruction(ix: TransactionInstruction): Buffer {
const programId = ix.programId.toBuffer();
const accountsLen = Buffer.alloc(2);
accountsLen.writeUInt16BE(ix.keys.length);
const accounts = Buffer.concat(ix.keys.map((account) => {
const isSigner = Buffer.alloc(1);
isSigner.writeUInt8(account.isSigner ? 1 : 0);
const isWritable = Buffer.alloc(1);
isWritable.writeUInt8(account.isWritable ? 1 : 0);
const pubkey = account.pubkey.toBuffer();
return Buffer.concat([pubkey, isSigner, isWritable]);
}))
const dataLen = Buffer.alloc(2);
dataLen.writeUInt16BE(ix.data.length);
return Buffer.concat([programId, accountsLen, accounts, dataLen, ix.data]);
}

export function appendGovernanceHeader(data: Buffer, governanceProgramId: PublicKey): Buffer {
const module = Buffer.from("GeneralPurposeGovernance".padStart(32, "\0"));
const action = Buffer.alloc(1);
action.writeUInt8(2); // SolanaCall
const chainId = Buffer.alloc(2);
chainId.writeUInt16BE(1); // solana
const programId = governanceProgramId.toBuffer();
return Buffer.concat([module, action, chainId, programId, data]);
}

// sentinel values used in governance
export const OWNER = new PublicKey(Buffer.from("owner".padEnd(32, "\0")));
export const PAYER = new PublicKey(Buffer.from("payer".padEnd(32, "\0")));

0 comments on commit c65563d

Please sign in to comment.