diff --git a/apps/desktop/src/components/AddressPill/AddressPill.tsx b/apps/desktop/src/components/AddressPill/AddressPill.tsx index f5310e4ab0..324261e904 100644 --- a/apps/desktop/src/components/AddressPill/AddressPill.tsx +++ b/apps/desktop/src/components/AddressPill/AddressPill.tsx @@ -35,6 +35,7 @@ export const AddressPill = memo( showIcons, addressKind, addressAlias, + address, onClick, elementRef, isMouseHover, @@ -109,7 +110,7 @@ export const AddressPill = memo( - {showIcons && ( + {showIcons && address.type !== "smart_rollup" && ( { operation.recipient.pkh, Number(operation.amount) ); + default: + throw new Error(`${operation.recipient.type} is not supported yet`); } // eslint-disable-next-line no-fallthrough case "fa1.2": diff --git a/packages/tezos/src/Address.test.ts b/packages/tezos/src/Address.test.ts new file mode 100644 index 0000000000..2f20917b5b --- /dev/null +++ b/packages/tezos/src/Address.test.ts @@ -0,0 +1,106 @@ +import { + isAddressValid, + isValidContractPkh, + isValidImplicitPkh, + isValidSmartRollupPkh, + parseContractPkh, + parseImplicitPkh, + parsePkh, + parseSmartRollupPkh, +} from "./Address"; +import { mockContractAddress, mockImplicitAddress, mockSmartRollupAddress } from "./testUtils"; + +describe("Address", () => { + const addresses = { + contract: mockContractAddress(0), + implicit: mockImplicitAddress(0), + smart_rollup: mockSmartRollupAddress(0), + }; + + describe("parsePkh", () => { + it.each(Object.entries(addresses))("parses %s address", (_, address) => { + expect(parsePkh(address.pkh)).toEqual(address); + }); + + it("throws error for invalid address", () => { + expect(() => parsePkh("invalid")).toThrow("Cannot parse address type: invalid"); + }); + }); + + describe("isAddressValid", () => { + it.each(Object.entries(addresses))("returns true for valid %s address", (_, address) => { + expect(isAddressValid(address.pkh)).toBe(true); + }); + + it("returns false for invalid address", () => { + expect(isAddressValid("invalid")).toBe(false); + }); + }); + + describe("isValidContractPkh", () => { + it.each([ + [true, addresses.contract.pkh], + [false, addresses.implicit.pkh], + [false, addresses.smart_rollup.pkh], + [false, "invalid"], + ])("returns %s for %s address", (expected, pkh) => { + expect(isValidContractPkh(pkh)).toBe(expected); + }); + }); + + describe("isValidImplicitPkh", () => { + it.each([ + [false, addresses.contract.pkh], + [true, addresses.implicit.pkh], + [false, addresses.smart_rollup.pkh], + [false, "invalid"], + ])("returns %s for %s address", (expected, pkh) => { + expect(isValidImplicitPkh(pkh)).toBe(expected); + }); + }); + + describe("isValidSmartRollupPkh", () => { + it.each([ + [false, addresses.contract.pkh], + [false, addresses.implicit.pkh], + [true, addresses.smart_rollup.pkh], + [false, "invalid"], + ])("returns %s for %s address", (expected, pkh) => { + expect(isValidSmartRollupPkh(pkh)).toBe(expected); + }); + }); + + describe("parse functions", () => { + const parseFunctions = { + parseContractPkh: { + fn: parseContractPkh, + validAddress: addresses.contract, + errorMessage: "Invalid contract address", + }, + parseImplicitPkh: { + fn: parseImplicitPkh, + validAddress: addresses.implicit, + errorMessage: "Invalid implicit address", + }, + parseSmartRollupPkh: { + fn: parseSmartRollupPkh, + validAddress: addresses.smart_rollup, + errorMessage: "Invalid smart rollup address", + }, + }; + + it.each(Object.entries(parseFunctions))( + "%s parses valid address", + (_, { fn, validAddress }) => { + expect(fn(validAddress.pkh)).toEqual(validAddress); + } + ); + + it.each(Object.entries(parseFunctions))( + "%s throws error for invalid address", + (_, { fn, errorMessage }) => { + expect(() => fn("invalid")).toThrow(`${errorMessage}: invalid`); + } + ); + }); +}); diff --git a/packages/tezos/src/Address.ts b/packages/tezos/src/Address.ts index 103c5560ab..aa12381adb 100644 --- a/packages/tezos/src/Address.ts +++ b/packages/tezos/src/Address.ts @@ -1,6 +1,11 @@ import { ValidationResult, validateAddress } from "@taquito/utils"; -import { type Address, type ContractAddress, type ImplicitAddress } from "./types"; +import { + type Address, + type ContractAddress, + type ImplicitAddress, + type SmartRollupAddress, +} from "./types"; export const parsePkh = (pkh: string): Address => { if (isValidContractPkh(pkh)) { @@ -9,14 +14,20 @@ export const parsePkh = (pkh: string): Address => { if (isValidImplicitPkh(pkh)) { return parseImplicitPkh(pkh); } + if (isValidSmartRollupPkh(pkh)) { + return parseSmartRollupPkh(pkh); + } throw new Error(`Cannot parse address type: ${pkh}`); }; export const isAddressValid = (pkh: string) => validateAddress(pkh) === ValidationResult.VALID; -export const isValidContractPkh = (pkh: string) => isAddressValid(pkh) && pkh.match(/^KT1\w+/); +export const isValidContractPkh = (pkh: string) => isAddressValid(pkh) && !!pkh.match(/^KT1\w+/); + +export const isValidImplicitPkh = (pkh: string) => + isAddressValid(pkh) && !!pkh.match(/^tz[1234]\w+/); -export const isValidImplicitPkh = (pkh: string) => isAddressValid(pkh) && pkh.match(/^tz[1234]\w+/); +export const isValidSmartRollupPkh = (pkh: string) => isAddressValid(pkh) && !!pkh.match(/^sr1\w+/); export const parseContractPkh = (pkh: string): ContractAddress => { if (isValidContractPkh(pkh)) { @@ -31,3 +42,10 @@ export const parseImplicitPkh = (pkh: string): ImplicitAddress => { } throw new Error(`Invalid implicit address: ${pkh}`); }; + +export const parseSmartRollupPkh = (pkh: string): SmartRollupAddress => { + if (isValidSmartRollupPkh(pkh)) { + return { type: "smart_rollup", pkh }; + } + throw new Error(`Invalid smart rollup address: ${pkh}`); +}; diff --git a/packages/tezos/src/testUtils.ts b/packages/tezos/src/testUtils.ts index 5a59af53d7..8ea6e5acba 100644 --- a/packages/tezos/src/testUtils.ts +++ b/packages/tezos/src/testUtils.ts @@ -1,4 +1,11 @@ -import { type ContractAddress, type ImplicitAddress } from "./types"; +import { type ContractAddress, type ImplicitAddress, type SmartRollupAddress } from "./types"; + +const validSmartRollupAddresses = ["sr1Ghq66tYK9y3r8CC1Tf8i8m5nxh8nTvZEf"]; + +export const mockSmartRollupAddress = (index: number): SmartRollupAddress => ({ + type: "smart_rollup", + pkh: validSmartRollupAddresses[index], +}); const validContractAddresses = [ "KT1QuofAgnsWffHzLA7D78rxytJruGHDe7XG", diff --git a/packages/tezos/src/types.ts b/packages/tezos/src/types.ts index 6840681423..863ae3d575 100644 --- a/packages/tezos/src/types.ts +++ b/packages/tezos/src/types.ts @@ -29,7 +29,12 @@ export type ImplicitAddress = { pkh: RawPkh; }; -export type Address = ContractAddress | ImplicitAddress; +export type SmartRollupAddress = { + type: "smart_rollup"; + pkh: RawPkh; +}; + +export type Address = ContractAddress | ImplicitAddress | SmartRollupAddress; export type Estimation = { storageLimit: number;