Skip to content

Commit

Permalink
Feature: Metamask Snaps (#103)
Browse files Browse the repository at this point in the history
* - Added metamask snap as a new NX package with cosmjs helpers for use with other cosmos based libraries including @sei-js
- Refactored helper functions in /core

* Finished MM snap and merged in main branch

* Added a separate babel build process for helper functions in metamask snap and removed helper functions from /core

* Added eslint directive for nodejs env

* Converted files to .ts

* Adjusted testMatch in snap config

* Added allowjs option for jest tests to snap tests

* Added chrome webdriver to action

* Added webdriver to coverage action

* Set max workers to 1
  • Loading branch information
codebycarson authored Dec 13, 2023
1 parent 15f0a37 commit 387cb59
Show file tree
Hide file tree
Showing 41 changed files with 9,635 additions and 3,014 deletions.
5 changes: 5 additions & 0 deletions .changeset/bright-shoes-attend.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'@sei-js/core': minor
---

Added helpful functions to wrap metamask snaps and use them as inputs to SeiWalletProvider
2 changes: 2 additions & 0 deletions .github/workflows/checks.yml
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,8 @@ jobs:
- uses: actions/setup-node@v3
with:
node-version: 18
- uses: browser-actions/setup-chrome@v1
- run: chrome --version
- run: yarn
- run: yarn lint:all
- run: yarn build:all
Expand Down
3 changes: 3 additions & 0 deletions .github/workflows/coverage.yml
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,9 @@ jobs:
with:
node-version: 18

- uses: browser-actions/setup-chrome@v1
- run: chrome --version

- name: Install dependencies
run: yarn install

Expand Down
1 change: 1 addition & 0 deletions .prettierignore
Original file line number Diff line number Diff line change
Expand Up @@ -2,3 +2,4 @@

/dist
/coverage

92 changes: 46 additions & 46 deletions package.json
Original file line number Diff line number Diff line change
@@ -1,48 +1,48 @@
{
"name": "sei-js",
"version": "1.1.5",
"license": "MIT",
"scripts": {
"build:all": "nx run-many --target=build",
"build:since": "nx affected --target=build",
"lint:all": "nx run-many --target=lint",
"lint:since": "nx affected --target=lint",
"test:all": "nx run-many --target=test",
"test:since": "nx affected --target=test",
"test:coverage": "nx affected:test --all --coverage --skip-nx-cache",
"release": "yarn build:all && changeset publish",
"release:internal": "yarn build:all && yarn changeset && yarn changeset version --snapshot internal && yarn changeset publish --no-git-tag --snapshot --tag internal",
"postrelease": "git push --follow-tags"
},
"private": true,
"dependencies": {
"@changesets/cli": "^2.26.0"
},
"devDependencies": {
"@nrwl/eslint-plugin-nx": "15.5.1",
"@nrwl/nx-cloud": "latest",
"@nrwl/workspace": "15.5.1",
"@types/jest": "^29.5.5",
"@types/node": "20.8.2",
"@typescript-eslint/eslint-plugin": "^5.50.0",
"@typescript-eslint/parser": "^5.50.0",
"eslint": "^8.33.0",
"eslint-config-prettier": "8.1.0",
"eslint-plugin-import": "2.26.0",
"eslint-plugin-jsx-a11y": "6.6.1",
"eslint-plugin-prettier": "^4.2.1",
"eslint-plugin-react": "7.31.11",
"eslint-plugin-react-hooks": "4.6.0",
"jest": "29.7.0",
"jest-environment-jsdom": "28.1.1",
"nx": "15.5.1",
"prettier": "^2.6.2",
"rimraf": "^3.0.2",
"ts-jest": "29.1.1",
"ts-node": "10.9.1",
"typescript": "~4.8.4"
},
"workspaces": [
"packages/*"
]
"name": "sei-js",
"version": "1.1.5",
"private": true,
"license": "MIT",
"workspaces": [
"packages/*"
],
"scripts": {
"build:all": "nx run-many --target=build",
"build:since": "nx affected --target=build",
"lint:all": "nx run-many --target=lint",
"lint:since": "nx affected --target=lint",
"release": "yarn build:all && changeset publish",
"postrelease": "git push --follow-tags",
"release:internal": "yarn build:all && yarn changeset && yarn changeset version --snapshot internal && yarn changeset publish --no-git-tag --snapshot --tag internal",
"test:all": "nx run-many --target=test",
"test:coverage": "nx affected:test --all --coverage --skip-nx-cache",
"test:since": "nx affected --target=test"
},
"dependencies": {
"@changesets/cli": "^2.26.0"
},
"devDependencies": {
"@nrwl/eslint-plugin-nx": "15.5.1",
"@nrwl/nx-cloud": "latest",
"@nrwl/workspace": "15.5.1",
"@types/jest": "^29.5.5",
"@types/node": "20.8.2",
"@typescript-eslint/eslint-plugin": "^5.50.0",
"@typescript-eslint/parser": "^5.50.0",
"eslint": "^8.33.0",
"eslint-config-prettier": "8.1.0",
"eslint-plugin-import": "2.26.0",
"eslint-plugin-jsx-a11y": "6.6.1",
"eslint-plugin-prettier": "^4.2.1",
"eslint-plugin-react": "7.31.11",
"eslint-plugin-react-hooks": "4.6.0",
"jest": "29.7.0",
"jest-environment-jsdom": "28.1.1",
"nx": "15.5.1",
"prettier": "^2.6.2",
"rimraf": "^3.0.2",
"ts-jest": "29.1.1",
"ts-node": "10.9.1",
"typescript": "~4.8.4"
}
}
50 changes: 25 additions & 25 deletions packages/core/package.json
Original file line number Diff line number Diff line change
@@ -1,33 +1,41 @@
{
"name": "@sei-js/core",
"version": "3.1.1",
"version": "0.0.0-internal-20231129052901",
"private": false,
"description": "TypeScript library for front end integrations with Sei",
"keywords": [
"sei",
"javascript",
"typescript"
],
"homepage": "https://github.com/sei-protocol/sei-js#readme",
"repository": "[email protected]:sei-protocol/sei-js.git",
"license": "MIT",
"sideEffects": false,
"exports": {
".": {
"import": "./dist/esm/index.js",
"require": "./dist/cjs/index.js",
"browser": {
"import": "./dist/esm/index-browser.js",
"require": "./dist/cjs/index-browser.js"
},
"types": "./dist/types/index.d.ts"
}
},
"main": "./dist/cjs/index.js",
"module": "./dist/esm/index.js",
"browser": "./dist/esm/index-browser.js",
"sideEffects": false,
"types": "./dist/types/index.d.ts",
"scripts": {
"prebuild": "rimraf dist",
"build": "yarn build:types && yarn build:cjs && yarn build:esm && yarn build:prettier",
"build:types": "tsc --project tsconfig.declarations.json",
"build:cjs": "BABEL_ENV=cjs babel src --out-dir dist/cjs --extensions '.js,.jsx,.ts,.tsx' --source-maps --copy-files --no-copy-ignored",
"build:esm": "BABEL_ENV=esm babel src --out-dir dist/esm --extensions '.js,.jsx,.ts,.tsx' --source-maps --copy-files --no-copy-ignored",
"build:prettier": "prettier --write 'dist/**/*.js'",
"build:types": "tsc --project tsconfig.declarations.json",
"test": "jest"
},
"homepage": "https://github.com/sei-protocol/sei-js#readme",
"keywords": [
"sei",
"javascript",
"typescript"
],
"repository": "[email protected]:sei-protocol/sei-js.git",
"license": "MIT",
"private": false,
"publishConfig": {
"access": "public"
},
"dependencies": {
"@cosmjs/amino": "^0.29.5",
"@cosmjs/cosmwasm-stargate": "^0.29.5",
Expand Down Expand Up @@ -58,15 +66,7 @@
"@types/elliptic": "^6.4.14",
"@types/sha.js": "^2.4.1"
},
"exports": {
".": {
"import": "./dist/esm/index.js",
"require": "./dist/cjs/index.js",
"browser": {
"import": "./dist/esm/index-browser.js",
"require": "./dist/cjs/index-browser.js"
},
"types": "./dist/types/index.d.ts"
}
"publishConfig": {
"access": "public"
}
}
6 changes: 3 additions & 3 deletions packages/core/src/lib/utils/__tests__/address.spec.ts
Original file line number Diff line number Diff line change
@@ -1,13 +1,13 @@
import { describe, expect, it } from '@jest/globals';
import { getAddressFromPubKey, isValidSeiAddress, pubKeyToBytes, pubKeyToKeyPair, verifyDigest32 } from '../address';
import { getAddressHashFromPubKey, isValidSeiAddress, pubKeyToBytes, pubKeyToKeyPair, verifyDigest32 } from '../address';
import { randomBytes } from 'crypto';

const MOCK_PUB_KEY = new Uint8Array([
4, 116, 1, 101, 176, 186, 244, 209, 5, 209, 19, 132, 244, 147, 197, 29, 25, 163, 111, 176, 167, 12, 14, 132, 111, 54, 27, 175, 58, 222, 9, 164, 17, 238, 10, 125,
251, 27, 86, 34, 144, 170, 53, 1, 39, 215, 43, 232, 73, 1, 141, 150, 35, 103, 128, 242, 240, 169, 107, 169, 102, 44, 226, 126, 14
]);

const MOCK_PUB_KEY_ADDRESS = new Uint8Array([78, 147, 198, 6, 144, 152, 234, 61, 25, 11, 200, 86, 12, 11, 124, 189, 203, 38, 208, 255]);
const MOCK_PUB_KEY_ADDRESS = new Uint8Array([238, 39, 110, 158, 63, 196, 185, 243, 87, 44, 63, 181, 99, 247, 136, 235, 53, 144, 126, 40]);

describe('isValidSeiAddress', () => {
it('should return true for a valid SEI address', () => {
Expand Down Expand Up @@ -79,7 +79,7 @@ describe('pubKeyToBytes', () => {

describe('getAddressFromPubKey', () => {
it('should return a valid address from a given public key', () => {
const address = getAddressFromPubKey(MOCK_PUB_KEY);
const address = getAddressHashFromPubKey(MOCK_PUB_KEY);

expect(address).toBeInstanceOf(Uint8Array);
expect(address).toEqual(MOCK_PUB_KEY_ADDRESS);
Expand Down
89 changes: 43 additions & 46 deletions packages/core/src/lib/utils/address.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,71 +2,68 @@ import { fromBech32 } from '@cosmjs/encoding';
import { ec as EllipticCurve } from 'elliptic';
import { sha256 } from './hash';
import { ripemd160 } from '@cosmjs/crypto';
import { toBech32 } from './bech32';

export const isValidSeiAddress = (address: string) => {
try {
const { prefix } = fromBech32(address);
return prefix && prefix === 'sei';
} catch (e) {
return false;
}
try {
const { prefix } = fromBech32(address);
return prefix && prefix === 'sei';
} catch (e) {
return false;
}
};

export const pubKeyToKeyPair = (pubKey: Uint8Array): EllipticCurve.KeyPair => {
const secp256k1 = new EllipticCurve('secp256k1');
const secp256k1 = new EllipticCurve('secp256k1');

return secp256k1.keyFromPublic(Buffer.from(pubKey).toString('hex'), 'hex');
return secp256k1.keyFromPublic(Buffer.from(pubKey).toString('hex'), 'hex');
};

export const pubKeyToBytes = (pubKey: Uint8Array, uncompressed?: boolean): Uint8Array => {
if (uncompressed && pubKey.length === 65) {
return pubKey;
}
if (!uncompressed && pubKey.length === 33) {
return pubKey;
}
if (uncompressed && pubKey.length === 65) {
return pubKey;
}
if (!uncompressed && pubKey.length === 33) {
return pubKey;
}

const keyPair = pubKeyToKeyPair(pubKey);
const keyPair = pubKeyToKeyPair(pubKey);

if (uncompressed) {
return new Uint8Array(Buffer.from(keyPair.getPublic().encode('hex', false), 'hex'));
} else {
return new Uint8Array(Buffer.from(keyPair.getPublic().encodeCompressed('hex'), 'hex'));
}
if (uncompressed) {
return new Uint8Array(Buffer.from(keyPair.getPublic().encode('hex', false), 'hex'));
} else {
return new Uint8Array(Buffer.from(keyPair.getPublic().encodeCompressed('hex'), 'hex'));
}
};

export const getAddressFromPubKey = (pubKey: Uint8Array): Uint8Array => {
const shaHash = sha256(pubKeyToBytes(pubKey));
let ripemdHash = ripemd160(shaHash); // Assuming ripemd160 takes Uint8Array and returns Uint8Array

// If ripemd160 returns a hex string, convert it to a Uint8Array
if (typeof ripemdHash === 'string') {
ripemdHash = new Uint8Array(Buffer.from(ripemdHash, 'hex'));
}
export const compressedPubKeyToAddress = (publicKey: Uint8Array) => {
return toBech32(getAddressHashFromPubKey(publicKey));
};

return ripemdHash;
export const getAddressHashFromPubKey = (compressedPublicKey: Uint8Array): Uint8Array => {
return ripemd160(sha256(compressedPublicKey));
};

export const verifyDigest32 = (digest: Uint8Array, signature: Uint8Array, pubKey: Uint8Array): boolean => {
if (digest.length !== 32) {
throw new Error(`Invalid length of digest to verify: ${digest.length}`);
}
if (digest.length !== 32) {
throw new Error(`Invalid length of digest to verify: ${digest.length}`);
}

if (signature.length !== 64) {
throw new Error(`Invalid length of signature: ${signature.length}`);
}
if (signature.length !== 64) {
throw new Error(`Invalid length of signature: ${signature.length}`);
}

const secp256k1 = new EllipticCurve('secp256k1');
const secp256k1 = new EllipticCurve('secp256k1');

const r = signature.slice(0, 32);
const s = signature.slice(32);
const r = signature.slice(0, 32);
const s = signature.slice(32);

return secp256k1.verify(
digest,
{
r: Buffer.from(r).toString('hex'),
s: Buffer.from(s).toString('hex')
},
pubKeyToKeyPair(pubKey)
);
return secp256k1.verify(
digest,
{
r: Buffer.from(r).toString('hex'),
s: Buffer.from(s).toString('hex')
},
pubKeyToKeyPair(pubKey)
);
};
6 changes: 4 additions & 2 deletions packages/core/src/lib/utils/index.ts
Original file line number Diff line number Diff line change
@@ -1,2 +1,4 @@
export { verifyArbitrary } from './signing';
export { isValidSeiAddress } from './address';
export { verifyArbitrary, makeADR36AminoSignDoc } from './signing';
export { toBech32 } from './bech32';
export { isValidSeiAddress, compressedPubKeyToAddress } from './address';
export { serializeAminoSignDoc, serializeDirectSignDoc } from './serialize';
18 changes: 18 additions & 0 deletions packages/core/src/lib/utils/serialize.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
import { StdSignDoc } from '@cosmjs/amino';
import { serializeSignDoc } from '@cosmjs/amino/build/signdoc';
import { SignDoc } from 'cosmjs-types/cosmos/tx/v1beta1/tx';

export function serializeDirectSignDoc(signDoc: SignDoc) {
return SignDoc.encode(
SignDoc.fromPartial({
accountNumber: signDoc.accountNumber.toString(),
authInfoBytes: signDoc.authInfoBytes,
bodyBytes: signDoc.bodyBytes,
chainId: signDoc.chainId
})
).finish();
}

export function serializeAminoSignDoc(signDoc: StdSignDoc) {
return serializeSignDoc(signDoc);
}
Loading

0 comments on commit 387cb59

Please sign in to comment.