Skip to content

Commit 3b2fddf

Browse files
committed
Implement instruction coder
1 parent 85e375a commit 3b2fddf

File tree

13 files changed

+1779
-0
lines changed

13 files changed

+1779
-0
lines changed

src/__tests__/Anchor.test.ts

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
import { AnchorProvider, Wallet } from "@coral-xyz/anchor"
2+
import { Connection, Keypair, PublicKey } from "@solana/web3.js"
3+
import { getPythProgramKeyForCluster, pythOracleProgram } from "../index"
4+
5+
test('Anchor', (done) => {
6+
jest.setTimeout(60000);
7+
const provider = new AnchorProvider(new Connection("https://api.mainnet-beta.solana.com"), new Wallet(new Keypair()), AnchorProvider.defaultOptions());
8+
const pythOracle = pythOracleProgram(getPythProgramKeyForCluster("mainnet-beta"), provider);
9+
pythOracle.methods.initMapping().accounts({fundingAccount : new PublicKey(0)}).instruction().then((x) => console.log(x));
10+
pythOracle.methods.addMapping().accounts({fundingAccount : new PublicKey(0)}).instruction().then((x) => console.log(x));
11+
done()
12+
13+
})

src/anchor/coder/accounts.ts

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
import { AccountsCoder, ACCOUNT_DISCRIMINATOR_SIZE, Idl } from "@coral-xyz/anchor";
2+
import { IdlTypeDef } from "@coral-xyz/anchor/dist/cjs/idl";
3+
4+
export class PythOracleAccountCoder<A extends string = string>
5+
implements AccountsCoder
6+
{
7+
constructor(private idl: Idl) {}
8+
9+
public async encode<T = any>(accountName: A, account: T): Promise<Buffer> {
10+
throw new Error("Not implemented");
11+
}
12+
13+
public decode<T = any>(accountName: A, ix: Buffer): T {
14+
throw new Error("Not implemented");
15+
}
16+
17+
public decodeUnchecked<T = any>(accountName: A, ix: Buffer): T {
18+
throw new Error("Not implemented");
19+
}
20+
21+
public memcmp(
22+
accountName: A,
23+
_appendData?: Buffer
24+
): { dataSize?: number; offset?: number; bytes?: string } {
25+
throw new Error("Not implemented");
26+
}
27+
28+
public size(idlAccount: IdlTypeDef): number {
29+
return 0;
30+
}
31+
32+
}

src/anchor/coder/events.ts

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
import { Idl, Event, EventCoder } from "@coral-xyz/anchor";
2+
import { IdlEvent } from "@coral-xyz/anchor/dist/cjs/idl";
3+
4+
export class PythOracleEventCoder implements EventCoder {
5+
constructor(_idl: Idl) {}
6+
7+
decode<E extends IdlEvent = IdlEvent, T = Record<string, string>>(
8+
_log: string
9+
): Event<E, T> | null {
10+
throw new Error("Pyth oracle program does not have events");
11+
}
12+
}

src/anchor/coder/idl.ts

Lines changed: 167 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,167 @@
1+
import camelCase from "camelcase";
2+
import { Layout } from "buffer-layout";
3+
import * as borsh from "@coral-xyz/borsh";
4+
import { IdlEnumVariant, IdlField, IdlType, IdlTypeDef } from "@coral-xyz/anchor/dist/cjs/idl";
5+
import { IdlError } from "@coral-xyz/anchor";
6+
7+
export class IdlCoder {
8+
public static fieldLayout(
9+
field: { name?: string } & Pick<IdlField, "type">,
10+
types?: IdlTypeDef[]
11+
): Layout {
12+
const fieldName =
13+
field.name !== undefined ? camelCase(field.name) : undefined;
14+
switch (field.type) {
15+
case "bool": {
16+
return borsh.bool(fieldName);
17+
}
18+
case "u8": {
19+
return borsh.u8(fieldName);
20+
}
21+
case "i8": {
22+
return borsh.i8(fieldName);
23+
}
24+
case "u16": {
25+
return borsh.u16(fieldName);
26+
}
27+
case "i16": {
28+
return borsh.i16(fieldName);
29+
}
30+
case "u32": {
31+
return borsh.u32(fieldName);
32+
}
33+
case "i32": {
34+
return borsh.i32(fieldName);
35+
}
36+
case "f32": {
37+
return borsh.f32(fieldName);
38+
}
39+
case "u64": {
40+
return borsh.u64(fieldName);
41+
}
42+
case "i64": {
43+
return borsh.i64(fieldName);
44+
}
45+
case "f64": {
46+
return borsh.f64(fieldName);
47+
}
48+
case "u128": {
49+
return borsh.u128(fieldName);
50+
}
51+
case "i128": {
52+
return borsh.i128(fieldName);
53+
}
54+
case "u256": {
55+
return borsh.u256(fieldName);
56+
}
57+
case "i256": {
58+
return borsh.i256(fieldName);
59+
}
60+
case "bytes": {
61+
return borsh.vecU8(fieldName);
62+
}
63+
case "string": {
64+
return borsh.str(fieldName);
65+
}
66+
case "publicKey": {
67+
return borsh.publicKey(fieldName);
68+
}
69+
default: {
70+
if ("vec" in field.type) {
71+
return borsh.vec(
72+
IdlCoder.fieldLayout(
73+
{
74+
name: undefined,
75+
type: field.type.vec,
76+
},
77+
types
78+
),
79+
fieldName
80+
);
81+
} else if ("option" in field.type) {
82+
return borsh.option(
83+
IdlCoder.fieldLayout(
84+
{
85+
name: undefined,
86+
type: field.type.option,
87+
},
88+
types
89+
),
90+
fieldName
91+
);
92+
} else if ("defined" in field.type) {
93+
const defined = field.type.defined;
94+
// User defined type.
95+
if (types === undefined) {
96+
throw new IdlError("User defined types not provided");
97+
}
98+
const filtered = types.filter((t) => t.name === defined);
99+
if (filtered.length !== 1) {
100+
throw new IdlError(`Type not found: ${JSON.stringify(field)}`);
101+
}
102+
return IdlCoder.typeDefLayout(filtered[0], types, fieldName);
103+
} else if ("array" in field.type) {
104+
let arrayTy = field.type.array[0];
105+
let arrayLen = field.type.array[1];
106+
let innerLayout = IdlCoder.fieldLayout(
107+
{
108+
name: undefined,
109+
type: arrayTy,
110+
},
111+
types
112+
);
113+
return borsh.array(innerLayout, arrayLen, fieldName);
114+
} else {
115+
throw new Error(`Not yet implemented: ${field}`);
116+
}
117+
}
118+
}
119+
}
120+
121+
public static typeDefLayout(
122+
typeDef: IdlTypeDef,
123+
types: IdlTypeDef[] = [],
124+
name?: string
125+
): Layout {
126+
if (typeDef.type.kind === "struct") {
127+
const fieldLayouts = typeDef.type.fields.map((field) => {
128+
const x = IdlCoder.fieldLayout(field, types);
129+
return x;
130+
});
131+
return borsh.struct(fieldLayouts, name);
132+
} else if (typeDef.type.kind === "enum") {
133+
let variants = typeDef.type.variants.map((variant: IdlEnumVariant) => {
134+
const name = camelCase(variant.name);
135+
if (variant.fields === undefined) {
136+
return borsh.struct([], name);
137+
}
138+
const fieldLayouts = variant.fields.map(
139+
(f: IdlField | IdlType, i: number) => {
140+
if (!f.hasOwnProperty("name")) {
141+
return IdlCoder.fieldLayout(
142+
{ type: f as IdlType, name: i.toString() },
143+
types
144+
);
145+
}
146+
// this typescript conversion is ok
147+
// because if f were of type IdlType
148+
// (that does not have a name property)
149+
// the check before would've errored
150+
return IdlCoder.fieldLayout(f as IdlField, types);
151+
}
152+
);
153+
return borsh.struct(fieldLayouts, name);
154+
});
155+
156+
if (name !== undefined) {
157+
// Buffer-layout lib requires the name to be null (on construction)
158+
// when used as a field.
159+
return borsh.rustEnum(variants).replicate(name);
160+
}
161+
162+
return borsh.rustEnum(variants, name);
163+
} else {
164+
throw new Error(`Unknown type kint: ${typeDef}`);
165+
}
166+
}
167+
}

src/anchor/coder/index.ts

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
import { Idl, Coder } from "@coral-xyz/anchor";
2+
3+
import { PythOracleEventCoder } from "./events";
4+
import { PythOracleAccountCoder } from "./accounts"
5+
import { PythOracleInstructionCoder } from "./instructions";
6+
import { PythOracleStateCoder } from "./state";
7+
import { PythOracleTypesCoder } from "./types";
8+
9+
10+
/**
11+
* Coder for PythOracleCoder
12+
*/
13+
export class PythOracleCoder implements Coder {
14+
readonly accounts: PythOracleAccountCoder;
15+
readonly events: PythOracleEventCoder;
16+
readonly instruction: PythOracleInstructionCoder;
17+
readonly state: PythOracleStateCoder;
18+
readonly types: PythOracleTypesCoder;
19+
20+
constructor(idl: Idl) {
21+
this.accounts = new PythOracleAccountCoder(idl);
22+
this.events = new PythOracleEventCoder(idl);
23+
this.instruction = new PythOracleInstructionCoder(idl);
24+
this.state = new PythOracleStateCoder(idl);
25+
this.types = new PythOracleTypesCoder(idl);
26+
}
27+
}

0 commit comments

Comments
 (0)