import BN from "bn.js"; import { expect } from "chai"; import { ActionVertexInfo, NonSpecificActionGroup, NonSpecificConstruction, } from "../lib"; import { deserializeNonspecificConstruction, serializeNonspecificConstruction, } from "../lib/serializer/index"; import { Provider } from "../../../../../anchor/ts/src"; import { Account, PublicKey } from "@solana/web3.js"; import { buildSeqListOfActionCalls, getInDegrees, topologicalSort, _convertToDoubleAdjacencyList, } from "../lib/graph-utils"; // import { mergeConstructions } from "../lib/builder"; const pk1 = new Account().publicKey.toBase58(); const pk2 = new Account().publicKey.toBase58(); const pk3 = new Account().publicKey.toBase58(); const pk4 = new Account().publicKey.toBase58(); const pk5 = new Account().publicKey.toBase58(); const pk6 = new Account().publicKey.toBase58(); const actionGroupWithIsolatedAction: NonSpecificActionGroup = { first: "passthrough", actions: { passthrough1: { actionTypeUID: "Transfer", actionPID: pk2, inputs: { recipientTokenAccount: pk3, }, nextActions: [{ "transfer-1": 1 }], }, passthrough: { actionTypeUID: "Transfer", actionPID: pk2, inputs: { recipientTokenAccount: pk3, }, nextActions: [], }, }, }; describe("Test builder", () => { const constr1: NonSpecificConstruction = { actionDatas: { "passthrough-1": { actionGroup: { first: "passthrough", actions: { passthrough: { actionTypeUID: "Transfer", actionPID: pk2, inputs: { recipientTokenAccount: pk3, }, nextActions: [{ "transfer-1": 1 }], }, }, }, }, "transfer-1": { action: { actionTypeUID: "Transfer", actionPID: pk2, inputs: { recipientTokenAccount: pk3, }, nextActions: [], }, }, }, initialActions: ["passthrough-1"], initialSplits: [1], amounts: ["100"], initialMints: [PublicKey.default], }; const constr2: NonSpecificConstruction = { actionDatas: { "passthrough-1": { actionGroup: { first: "passthrough", actions: { internal1: { actionTypeUID: "Transfer", actionPID: pk2, inputs: { recipientTokenAccount: pk3, }, nextActions: [{ passthrough2: 1 }], }, passthrough: { actionTypeUID: "Transfer", actionPID: pk2, inputs: { recipientTokenAccount: pk3, }, nextActions: [{ internal1: 1 }], }, passthrough2: { actionTypeUID: "Transfer", actionPID: pk2, inputs: { recipientTokenAccount: pk3, }, nextActions: [{ "transfer-1": 1, "transfer-2": 1 }], }, }, }, }, "transfer-1": { action: { actionTypeUID: "Transfer", actionPID: pk2, inputs: { recipientTokenAccount: pk3, }, nextActions: [{ "transfer-2": 1 }, { "transfer-2": 1 }], }, }, "transfer-2": { action: { actionTypeUID: "Transfer", actionPID: pk2, inputs: { recipientTokenAccount: pk3, }, nextActions: [], }, }, }, initialActions: ["passthrough-1", "transfer-1"], initialSplits: [1], amounts: ["100"], initialMints: [PublicKey.default], }; const constr3: NonSpecificConstruction = { actionDatas: { "passthrough-1": { actionGroup: { first: "passthrough", actions: { passthrough: { actionTypeUID: "Transfer", actionPID: pk2, inputs: { recipientTokenAccount: pk3, }, nextActions: [{ "transfer-1": 1 }, { passthrough2: 1 }], }, passthrough2: { actionTypeUID: "Transfer", actionPID: pk2, inputs: { recipientTokenAccount: pk3, }, nextActions: [{ "transfer-3": 1 }], }, }, }, }, "transfer-3": { action: { actionTypeUID: "Transfer", actionPID: pk2, inputs: { recipientTokenAccount: pk3, }, nextActions: [{ "transfer-1": 1 }], }, }, "group-2": { actionGroup: { first: "passthrough21", actions: { passthrough21: { actionTypeUID: "Transfer", actionPID: pk2, inputs: { recipientTokenAccount: pk3, }, nextActions: [{ passthrough22: 1, passthrough23: 1 }], }, passthrough22: { actionTypeUID: "Transfer", actionPID: pk2, inputs: { recipientTokenAccount: pk3, }, nextActions: [{ passthrough23: 1 }], }, passthrough23: { actionTypeUID: "Transfer", actionPID: pk2, inputs: { recipientTokenAccount: pk3, }, nextActions: [], }, }, }, }, "transfer-1": { action: { actionTypeUID: "Transfer", actionPID: pk2, inputs: { recipientTokenAccount: pk3, }, nextActions: [{ "group-2": 1 }], }, }, }, initialActions: ["passthrough-1"], initialSplits: [1], amounts: ["100"], initialMints: [PublicKey.default], }; it("Should test building the seq list of action calls", () => { const { order: order1 } = buildSeqListOfActionCalls( constr1.actionDatas, constr1.initialActions, 1 ); expect(order1).deep.eq([[0], [1]]); const { order: order2 } = buildSeqListOfActionCalls( constr2.actionDatas, constr2.initialActions, 1 ); expect(order2).deep.eq([[3], [1, 0, 2], [4]]); const { order: order3 } = buildSeqListOfActionCalls( constr3.actionDatas, constr3.initialActions, 1 ); expect(order3).deep.eq([[0, 1], [2], [6], [3, 4, 5]]); }); it("Should test helper functions", () => { const ret1 = _convertToDoubleAdjacencyList( constr1.actionDatas, Object.keys(constr1.actionDatas), constr1.initialActions ); expect(ret1.graph).deep.eq([[[1]], []]); expect(ret1.initActions).deep.eq([0]); const inDegrees1 = getInDegrees(ret1.graph); expect(inDegrees1).deep.eq([0, 1]); const sorted1 = topologicalSort(ret1.graph, inDegrees1, [0]); expect(sorted1).deep.eq([0, 1]); const ret2 = _convertToDoubleAdjacencyList( constr2.actionDatas, Object.keys(constr2.actionDatas), constr2.initialActions ); expect(ret2.graph).deep.eq([[[], [], [1, 2]], [[2], [2]], []]); expect(ret2.initActions).deep.eq([0, 1]); const inDegrees2 = getInDegrees(ret2.graph); expect(inDegrees2).deep.eq([0, 1, 3]); const sorted2 = topologicalSort(ret2.graph, inDegrees2, [0]); expect(sorted2).deep.eq([0, 1, 2]); // TODO: more test }); it("Should test that a construction with an action group containing an unhit action throws an error", () => { const constr: NonSpecificConstruction = { ...constr1 }; constr.actionDatas["bad-group"] = { actionGroup: actionGroupWithIsolatedAction, }; expect(() => _convertToDoubleAdjacencyList( constr.actionDatas, Object.keys(constr.actionDatas), ["bad-group"] ) ).to.throw("Expected no actions to be left uncalled"); }); }); // xdescribe("Test builder merge", () => { // const constructionParent: NonSpecificConstruction = { // actionDatas: { // "transfer-1": { // action: { // actionTypeUID: "Transfer", // actionPID: pk2, // inputs: { // recipientTokenAccount: pk3, // }, // nextActions: [], // }, // }, // }, // initialActionIndices: [0], // initialSplits: [1], // amount: "100", // }; // const constructionChild: NonSpecificConstruction = { // actionDatas: [ // { // actionTypeUID: "Transfer", // actionPID: pk5, // buildActionInputs: { // recipientTokenAccount: pk6, // }, // actionVertexInfo: { // nextNodes: [ // [ // { // fraction: 1, nextInputMintIdx: 0, // actionIdx: 1, // }, // ], // ], // inDegree: 1, // }, // }, // ], // initialActionIndices: [0], // initialSplits: [1], // amount: "100", // }; // it("Should combine two non specific constructions where the child appends to the parents first action", () => { // const merged = mergeConstructions(constructionParent, constructionChild, [ // { indexInParent: { actionIdx: 0, nextNodeIdx: 0 }, splitFrac: 100 }, // ]); // const expected = { // actionDatas: [ // { // actionTypeUID: "Transfer", // actionPID: pk2, // buildActionInputs: { // recipientTokenAccount: pk3, // }, // actionVertexInfo: { // nextNodes: [ // [ // { // fraction: 1, nextInputMintIdx: 000, // actionIdx: 1, // }, // ], // ], // inDegree: 1, // }, // }, // { // actionTypeUID: "Transfer", // actionPID: pk5, // buildActionInputs: { // recipientTokenAccount: pk6, // }, // actionVertexInfo: { // nextNodes: [ // [ // { // fraction: 1, nextInputMintIdx: 0, // actionIdx: 2, // }, // ], // ], // inDegree: 1, // }, // }, // ], // initialActionIndices: [0], // initialSplits: [1], // amount: "100", // }; // expect(merged).deep.eq(expected); // }); // it("Should combine two non specific constructions where the child appends to the initial", () => { // const merged = mergeConstructions(constructionParent, constructionChild, [ // { appendToInitial: true, splitFrac: 100 }, // ]); // const expected = { // actionDatas: [ // { // actionTypeUID: "Transfer", // actionPID: pk2, // buildActionInputs: { // recipientTokenAccount: pk3, // }, // actionVertexInfo: { // nextNodes: [[]], // inDegree: 1, // }, // }, // { // actionTypeUID: "Transfer", // actionPID: pk5, // buildActionInputs: { // recipientTokenAccount: pk6, // }, // actionVertexInfo: { // nextNodes: [ // [ // { // fraction: 1, nextInputMintIdx: 0, // actionIdx: 2, // }, // ], // ], // inDegree: 1, // }, // }, // ], // initialActionIndices: [0, 1], // initialSplits: [1, 100], // amount: "100", // }; // expect(merged).deep.eq(expected); // }); // });