Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: encode principal to Eth address as hex #524

Merged
merged 6 commits into from
Jan 18, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions packages/cketh/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,3 +4,4 @@ export type {
} from "../candid/minter";
export * from "./errors/minter.errors";
export { CkETHMinterCanister } from "./minter.canister";
export * from "./utils/minter.utils";
36 changes: 36 additions & 0 deletions packages/cketh/src/utils/minter.utils.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
import { Principal } from "@dfinity/principal";
import { encodePrincipalToEthAddress } from "./minter.utils";

describe("minter-utils", () => {
const mockPrincipalExample1 = Principal.from(
"esd5n-wtdqq-kimdw-qrxjr-7luer-zjhez-hsauj-pu5ox-2y6ov-g53g4-xqe",
);
const ethExample1 =
"0x1d638414860ed08dd31fae848e527264f20512fa75d7d63cea9bbb372f020000";

const mockPrincipalExample2 = Principal.from(
"auycm-ouxjf-el73p-7cfpm-73w3q-fd7xc-ubevq-ea4vt-vpe2n-rs7e5-qqe",
);
const ethExample2 =
"0x1d974948bfedff115ecfeedb8147fb8a8125604072b3abc9a6c65f2761020000";

const mockPrincipalExample3 = Principal.from(
"ouoed-xoejt-4ssko-nf4tu-w3wbu-x2lz7-qfili-67oyv-kz4kf-nmo7q-yqe",
);
const ethExample3 =
"0x1dc44cf92929cd2f274b6ec1a5f4bcfe0542d1efbb155678a2b58efc31020000";

it("should encode principal into fixed 32-byte representation suitable for calling Ethereum smart contracts", () => {
expect(encodePrincipalToEthAddress(mockPrincipalExample1)).toEqual(
ethExample1,
);

expect(encodePrincipalToEthAddress(mockPrincipalExample2)).toEqual(
ethExample2,
);

expect(encodePrincipalToEthAddress(mockPrincipalExample3)).toEqual(
ethExample3,
);
});
});
45 changes: 45 additions & 0 deletions packages/cketh/src/utils/minter.utils.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
import type { Principal } from "@dfinity/principal";
import { decodeBase32 } from "@dfinity/utils";

/**
* Encode a principal to a byte array as Ethereum data hex (staring with 0x).
* Such a conversion is required to deposit ETH to the ckETH helper contract.
*
* Code adapted from the ckETH minter dashboard JS function: https://github.com/dfinity/ic/blob/master/rs/ethereum/cketh/minter/templates/principal_to_bytes.js
*
* @param principal The principal to encode into a fixed 32-byte representation suitable for calling Ethereum smart contracts.
*/
export const encodePrincipalToEthAddress = (principal: Principal): string => {
const rawBytes = decodeBase32(principal.toText().replace(/-/g, ""));
return bytes32Encode(rawBytes.slice(4));
};

/**
* Appends a hex representation of a number to string.
* @param {string} s A string to append the hex to.
* @param {number} b A byte.
* @return {string} An updated string.
*/
const appendHexByte = (s: string, b: number): string => {
s += ((b >> 4) & 0x0f).toString(16);
s += (b & 0x0f).toString(16);
return s;
};

/**
* Encodes a byte array as Ethereum data hex (staring with 0x).
* @param {Array<number>} bytes A byte array.
* @return {string} A hex string.
*/
const bytes32Encode = (bytes: Uint8Array): string => {
const n = bytes.length;
let s = "0x";
s = appendHexByte(s, n);
for (let i = 0; i < bytes.length; i++) {
s = appendHexByte(s, bytes[i]);
}
for (let i = 0; i < 31 - bytes.length; i++) {
s += "00";
}
return s;
};
5 changes: 4 additions & 1 deletion scripts/docs.js
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,10 @@ const ckBTCInputFiles = [
"./packages/ckbtc/src/utils/btc.utils.ts",
];

const ckETHInputFiles = ["./packages/cketh/src/minter.canister.ts"];
const ckETHInputFiles = [
"./packages/cketh/src/minter.canister.ts",
"./packages/ledger-icrc/src/utils/minter.utils.ts",
];

const icMgmtInputFiles = [
"./packages/ic-management/src/ic-management.canister.ts",
Expand Down
Loading