forked from chainapsis/keplr-wallet
-
Notifications
You must be signed in to change notification settings - Fork 31
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat: metamask private key migration (#9)
- Loading branch information
1 parent
27dbad2
commit cda3ee3
Showing
14 changed files
with
496 additions
and
2 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
12 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,4 +1,3 @@ | ||
@@ -1,11 +0,0 @@ | ||
**/*.png | ||
**/*.svg | ||
**/*.ttf | ||
|
@@ -12,3 +11,4 @@ dist/* | |
prod/* | ||
|
||
yarn.lock | ||
.nvmrc |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
build/* |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
build/* |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,33 @@ | ||
{ | ||
"name": "@fetchai/eth-migration", | ||
"version": "0.8.8", | ||
"main": "build/index.js", | ||
"author": "chainapsis", | ||
"license": "Apache-2.0", | ||
"private": false, | ||
"publishConfig": { | ||
"access": "public" | ||
}, | ||
"scripts": { | ||
"clean": "rm -rf node_modules; rm -rf build", | ||
"build": "tsc", | ||
"dev": "tsc -w", | ||
"test": "mocha --timeout 30000 -r ts-node/register 'src/**/*.spec.ts'", | ||
"lint-test": "eslint \"src/**/*\" && prettier --check \"src/**/*\"", | ||
"lint-fix": "eslint --fix \"src/**/*\" && prettier --write \"src/**/*\"" | ||
}, | ||
"devDependencies": { | ||
"mocha": "^8.2.1", | ||
"@types/keccak": "^3.0.1" | ||
}, | ||
"dependencies": { | ||
"buffer": "^5.4.3", | ||
"crypto-js": "^4.0.0", | ||
"elliptic": "^6.5.3", | ||
"sha.js": "^2.4.11", | ||
"keccak": "^3.0.1", | ||
"web3-utils": "^1.3.6", | ||
"@keplr-wallet/crypto": "^0.8.8", | ||
"@keplr-wallet/cosmos": "^0.8.8" | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,40 @@ | ||
import "mocha"; | ||
import assert from "assert"; | ||
import { Buffer } from "buffer"; | ||
import { ethPublicKeyToAddress, parseEthPrivateKey } from "./index"; | ||
|
||
describe("Test eth private key matches", () => { | ||
it("priv key should be processed", () => { | ||
const privateKey = | ||
"25ee227d82c2222d7e27aa61bbafd644ea949a817532471be2de38bf149071f3"; | ||
const expectedAddress = "0x742Fc9aFb9e1D8b474Da7b8a21Fd74f3E1633898"; | ||
const expectedRawPubKey = | ||
"ee7bfb4e1a7346f283f568dc248dd7fc1319412cbaae90df66eabab1c41064f95e2d22ac2f41c720f286759df596817a2c7ce7b1e18febaf705fb5ff8db70ee6"; | ||
const expectedCompressedPubKey = | ||
"02ee7bfb4e1a7346f283f568dc248dd7fc1319412cbaae90df66eabab1c41064f9"; | ||
|
||
const parsed = parseEthPrivateKey(Buffer.from(privateKey, "hex")); | ||
assert.strictEqual(parsed?.ethAddress, expectedAddress); | ||
|
||
assert.strictEqual( | ||
Buffer.from(parsed?.rawPublicKey).toString("hex"), | ||
expectedRawPubKey | ||
); | ||
|
||
assert.strictEqual( | ||
Buffer.from(parsed?.compressedPublicKey).toString("hex"), | ||
expectedCompressedPubKey | ||
); | ||
}); | ||
|
||
it("can generate a fetch address from a public key", () => { | ||
const publicKey = | ||
"02ee7bfb4e1a7346f283f568dc248dd7fc1319412cbaae90df66eabab1c41064f9"; | ||
const expectedFetchAddress = "fetch1s3rfc2n0cjgtqm4e2llvcjtq89yf6q7p2pfxw7"; | ||
|
||
assert.strictEqual( | ||
ethPublicKeyToAddress(Buffer.from(publicKey, "hex")), | ||
expectedFetchAddress | ||
); | ||
}); | ||
}); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,73 @@ | ||
import EC from "elliptic"; | ||
import keccak from "keccak"; | ||
import Web3Utils from "web3-utils"; | ||
import { PubKeySecp256k1 } from "@keplr-wallet/crypto"; | ||
import { Bech32Address } from "@keplr-wallet/cosmos"; | ||
|
||
const DEFAULT_BECH_PREFIX: string = "fetch"; | ||
|
||
export interface ParsedEthKey { | ||
ethAddress: string; | ||
rawPublicKey: Uint8Array; | ||
compressedPublicKey: Uint8Array; | ||
} | ||
|
||
export function parseEthPrivateKey( | ||
privateKey: Uint8Array | ||
): ParsedEthKey | undefined { | ||
if (privateKey.length !== 32) { | ||
return; | ||
} | ||
|
||
// parse the private key | ||
const secp256k1 = new EC.ec("secp256k1"); | ||
const key = secp256k1.keyFromPrivate(privateKey); | ||
const rawPublicKey = new Uint8Array( | ||
key.getPublic().encode("array", false).slice(1) | ||
); | ||
const compressedPublicKey = new Uint8Array( | ||
key.getPublic().encodeCompressed("array") | ||
); | ||
|
||
// generate the raw address | ||
const rawAddress = `0x${keccak("keccak256") | ||
.update(Buffer.from(rawPublicKey)) | ||
.digest("hex") | ||
.slice(24)}`; | ||
|
||
// compute the checksum address | ||
const ethAddress = Web3Utils.toChecksumAddress(rawAddress); | ||
|
||
// return the parsed output | ||
return { | ||
ethAddress, | ||
rawPublicKey, | ||
compressedPublicKey, | ||
}; | ||
} | ||
|
||
export function addressMatchesPrivateKey( | ||
address: string, | ||
privateKey: Uint8Array | ||
): boolean { | ||
const parsed = parseEthPrivateKey(privateKey); | ||
if (parsed === undefined) { | ||
return false; | ||
} | ||
|
||
return parsed.ethAddress === address; | ||
} | ||
|
||
export function ethPublicKeyToAddress( | ||
compressedPubKey: Uint8Array, | ||
prefix?: string | ||
): string | undefined { | ||
if (compressedPubKey.length != 33) { | ||
return; | ||
} | ||
|
||
const pubKey = new PubKeySecp256k1(compressedPubKey); | ||
const address = new Bech32Address(pubKey.getAddress()); | ||
|
||
return address.toBech32(prefix ?? DEFAULT_BECH_PREFIX); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,12 @@ | ||
{ | ||
"extends": "../../tsconfig.json", | ||
"compilerOptions": { | ||
"baseUrl": ".", | ||
"outDir": "build", | ||
"declaration": true, | ||
"rootDir": "src" | ||
}, | ||
"include": [ | ||
"src/**/*" | ||
] | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,75 @@ | ||
import React, { FunctionComponent, useState } from "react"; | ||
import { RegisterConfig } from "@keplr-wallet/hooks"; | ||
import { observer } from "mobx-react-lite"; | ||
import { FormattedMessage } from "react-intl"; | ||
import { Button } from "reactstrap"; | ||
import { BackButton } from "../index"; | ||
import { MigrateMetamaskPrivateKeyPage } from "./metamask-privatekey"; | ||
|
||
export const TypeMigrateEth = "migrate-from-eth"; | ||
|
||
enum MigrationMode { | ||
SELECT_MODE, | ||
METAMASK_PRIVATE_KEY, | ||
} | ||
|
||
export const MigrateEthereumAddressIntro: FunctionComponent<{ | ||
registerConfig: RegisterConfig; | ||
}> = observer(({ registerConfig }) => { | ||
return ( | ||
<Button | ||
color="primary" | ||
outline | ||
block | ||
onClick={(e) => { | ||
e.preventDefault(); | ||
|
||
registerConfig.setType(TypeMigrateEth); | ||
}} | ||
> | ||
<FormattedMessage id="register.eth-migrate.title" /> | ||
</Button> | ||
); | ||
}); | ||
|
||
const MigrationSelectionPage: FunctionComponent<{ | ||
setMode: (mode: MigrationMode) => void; | ||
onBack: () => void; | ||
}> = (props) => { | ||
return ( | ||
<div> | ||
<Button | ||
color="primary" | ||
outline | ||
block | ||
onClick={() => props.setMode(MigrationMode.METAMASK_PRIVATE_KEY)} | ||
> | ||
<FormattedMessage id="register.eth-migrate.metamask-private-key.title" /> | ||
</Button> | ||
<BackButton onClick={props.onBack} /> | ||
</div> | ||
); | ||
}; | ||
|
||
export const MigrateEthereumAddressPage: FunctionComponent<{ | ||
registerConfig: RegisterConfig; | ||
}> = observer(({ registerConfig }) => { | ||
const [mode, setMode] = useState<MigrationMode>(MigrationMode.SELECT_MODE); | ||
|
||
switch (mode) { | ||
case MigrationMode.SELECT_MODE: | ||
return ( | ||
<MigrationSelectionPage | ||
setMode={setMode} | ||
onBack={() => registerConfig.clear()} | ||
/> | ||
); | ||
case MigrationMode.METAMASK_PRIVATE_KEY: | ||
return ( | ||
<MigrateMetamaskPrivateKeyPage | ||
registerConfig={registerConfig} | ||
onBack={() => setMode(MigrationMode.SELECT_MODE)} | ||
/> | ||
); | ||
} | ||
}); |
Oops, something went wrong.