Skip to content

Commit

Permalink
Add beaconUtils to @umami/core
Browse files Browse the repository at this point in the history
  • Loading branch information
asiia-trilitech committed Aug 22, 2024
1 parent 1ef6712 commit ea478f1
Show file tree
Hide file tree
Showing 5 changed files with 478 additions and 26 deletions.
1 change: 1 addition & 0 deletions packages/core/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,7 @@
]
},
"dependencies": {
"@airgap/beacon-wallet": "^4.2.2",
"@taquito/rpc": "^20.0.1",
"@taquito/signer": "^20.0.1",
"@taquito/taquito": "^20.0.1",
Expand Down
176 changes: 176 additions & 0 deletions packages/core/src/beaconUtils.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,176 @@
import { type PartialTezosOperation, TezosOperationType } from "@airgap/beacon-wallet";
import { mockContractAddress, mockImplicitAddress } from "@umami/tezos";
import { without } from "lodash";

import { makeAccountOperations } from "./AccountOperations";
import { partialOperationToOperation, toAccountOperations } from "./beaconUtils";
import { mockImplicitAccount, mockTezOperation } from "./testUtils";

const account = mockImplicitAccount(1);

describe("toAccountOperations", () => {
it("throws if the list is empty", () => {
expect(() => toAccountOperations([], account)).toThrow("Empty operation details!");
});

it("converts a list of partial operations to ImplicitOperations", () => {
const operationDetails: PartialTezosOperation[] = [
{
kind: TezosOperationType.TRANSACTION,
amount: "1",
destination: mockImplicitAddress(2).pkh,
},
{
kind: TezosOperationType.TRANSACTION,
amount: "2",
destination: mockImplicitAddress(2).pkh,
},
];

const operations = toAccountOperations(operationDetails, account);

expect(operations).toEqual(
makeAccountOperations(account, account, [
{ type: "tez", amount: "1", recipient: mockImplicitAddress(2) },
{ type: "tez", amount: "2", recipient: mockImplicitAddress(2) },
])
);
});
});

describe("partialOperationToOperation", () => {
describe.each(
without(
Object.values(TezosOperationType),
TezosOperationType.TRANSACTION,
TezosOperationType.DELEGATION,
TezosOperationType.ORIGINATION
)
)("for %s", kind => {
it("throws an error", () => {
const operation: PartialTezosOperation = { kind } as PartialTezosOperation;

expect(() => partialOperationToOperation(operation, account)).toThrow(
`Unsupported operation kind: ${kind}`
);
});
});

test("tez transaction", () => {
const operation: PartialTezosOperation = {
kind: TezosOperationType.TRANSACTION,
amount: "1",
destination: mockImplicitAddress(2).pkh,
};

const result = partialOperationToOperation(operation, account);

expect(result).toEqual(mockTezOperation(1));
});

test("stake", () => {
const operation: PartialTezosOperation = {
kind: TezosOperationType.TRANSACTION,
amount: "1",
destination: mockImplicitAddress(2).pkh,
parameters: {
entrypoint: "stake",
value: [{ prim: "UNIT" }],
},
};

const result = partialOperationToOperation(operation, account);

expect(result).toEqual({
type: "stake",
amount: "1",
sender: mockImplicitAddress(2),
});
});

test("unstake", () => {
const operation: PartialTezosOperation = {
kind: TezosOperationType.TRANSACTION,
amount: "12",
destination: mockImplicitAddress(2).pkh,
parameters: {
entrypoint: "unstake",
value: [{ prim: "UNIT" }],
},
};

const result = partialOperationToOperation(operation, account);

expect(result).toEqual({
type: "unstake",
amount: "12",
sender: mockImplicitAddress(2),
});
});

test("finalize unstake", () => {
const operation: PartialTezosOperation = {
kind: TezosOperationType.TRANSACTION,
destination: mockImplicitAddress(2).pkh,
amount: "0",
parameters: {
entrypoint: "finalize_unstake",
value: [{ prim: "UNIT" }],
},
};

const result = partialOperationToOperation(operation, account);

expect(result).toEqual({
type: "finalize_unstake",
sender: mockImplicitAddress(2),
});
});

test("contract call", () => {
const operation: PartialTezosOperation = {
kind: TezosOperationType.TRANSACTION,
amount: "1",
destination: mockContractAddress(2).pkh,
parameters: {
entrypoint: "mockEntrypoint",
value: [{ prim: "UNIT" }],
},
};

const result = partialOperationToOperation(operation, account);

expect(result).toEqual({
type: "contract_call",
amount: "1",
contract: mockContractAddress(2),
entrypoint: "mockEntrypoint",
args: [{ prim: "UNIT" }],
});
});

test("delegate", () => {
const operation: PartialTezosOperation = {
kind: TezosOperationType.DELEGATION,
delegate: mockImplicitAddress(2).pkh,
};

const result = partialOperationToOperation(operation, account);

expect(result).toEqual({
type: "delegation",
sender: account.address,
recipient: mockImplicitAddress(2),
});
});

test("undelegate", () => {
const operation: PartialTezosOperation = {
kind: TezosOperationType.DELEGATION,
};

const result = partialOperationToOperation(operation, account);

expect(result).toEqual({ type: "undelegation", sender: account.address });
});
});
109 changes: 109 additions & 0 deletions packages/core/src/beaconUtils.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,109 @@
import { type PartialTezosOperation, TezosOperationType } from "@airgap/beacon-wallet";
import { isValidImplicitPkh, parseImplicitPkh, parsePkh } from "@umami/tezos";

import { type ImplicitAccount } from "./Account";
import { type ImplicitOperations } from "./AccountOperations";
import { type ContractOrigination, type Operation } from "./Operation";

/**
* takes a list of {@link PartialTezosOperation} which come from Beacon
* and converts them to {@link ImplicitOperations}
*
* @param operationDetails - the list of operations to convert
* @param signer - the {@link Account} that's going to sign the operation
* @returns
*/
export const toAccountOperations = (
operationDetails: PartialTezosOperation[],
signer: ImplicitAccount
): ImplicitOperations => {
if (operationDetails.length === 0) {
throw new Error("Empty operation details!");
}

const operations = operationDetails.map(operation =>
partialOperationToOperation(operation, signer)
);

return {
type: "implicit",
sender: signer,
operations,
signer,
};
};

/**
* Converts a {@link PartialTezosOperation} which comes from Beacon to a {@link Operation}
*
* Note: it doesn't supported all of the possible operation types, but only a subset of them.
*
* @param partialOperation - the operation to convert
* @param signer - the {@link Account} that's going to sign the operation
* @returns a parsed {@link Operation}
*/
export const partialOperationToOperation = (
partialOperation: PartialTezosOperation,
signer: ImplicitAccount
): Operation => {
switch (partialOperation.kind) {
case TezosOperationType.TRANSACTION: {
const { destination, amount, parameters } = partialOperation;
if (parameters) {
// if the destination is an implicit account then it's a pseudo operation
if (isValidImplicitPkh(destination)) {
switch (parameters.entrypoint) {
case "stake":
return { type: "stake", amount, sender: parseImplicitPkh(destination) };
case "unstake":
return { type: "unstake", amount, sender: parseImplicitPkh(destination) };
case "finalize_unstake":
return { type: "finalize_unstake", sender: parseImplicitPkh(destination) };
}
}

return {
type: "contract_call",
amount,
contract: parsePkh(destination),
entrypoint: parameters.entrypoint,
args: parameters.value,
};
}

return {
type: "tez",
amount,
recipient: parseImplicitPkh(partialOperation.destination),
};
}
case TezosOperationType.DELEGATION: {
const { delegate } = partialOperation;

if (delegate) {
return {
type: "delegation",
sender: signer.address,
recipient: parseImplicitPkh(delegate),
};
}
return { type: "undelegation", sender: signer.address };
}
case TezosOperationType.ORIGINATION: {
const { script } = partialOperation;
const { code, storage } = script as unknown as {
code: ContractOrigination["code"];
storage: ContractOrigination["storage"];
};

return {
type: "contract_origination",
sender: signer.address,
code,
storage,
};
}
default:
throw new Error(`Unsupported operation kind: ${partialOperation.kind}`);
}
};
1 change: 1 addition & 0 deletions packages/core/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,3 +10,4 @@ export * from "./Operation";
export * from "./testUtils";
export * from "./Token";
export * from "./TokenBalance";
export * from "./beaconUtils";
Loading

0 comments on commit ea478f1

Please sign in to comment.