Skip to content

Commit 9f7401d

Browse files
authored
feat: add account package (#61)
1 parent b4d9499 commit 9f7401d

File tree

19 files changed

+1201
-113
lines changed

19 files changed

+1201
-113
lines changed

.github/workflows/build-sdk.yml

+33-36
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,7 @@ jobs:
3535
working-directory: etd-sdk-generator
3636

3737
build-kotlin:
38-
needs: [build, test]
38+
needs: [ build, test ]
3939
runs-on: ubuntu-latest
4040
steps:
4141
- uses: actions/checkout@v2
@@ -56,29 +56,26 @@ jobs:
5656

5757

5858
build-typescript-npm:
59-
needs: [build, test]
59+
needs: [ build, test ]
6060
runs-on: ubuntu-latest
6161
steps:
6262
- uses: actions/checkout@v2
6363
- uses: actions/setup-node@v2
6464
with:
6565
node-version: 16
6666
registry-url: https://registry.npmjs.org/
67-
- run: yarn
67+
- run: yarn && yarn build
6868
name: Installing dependencies
6969
working-directory: sdk-dist/typescript
7070
- run: yarn test
7171
name: Testing
7272
working-directory: sdk-dist/typescript
73-
- run: yarn build
74-
name: Building dist
75-
working-directory: sdk-dist/typescript
7673
- run: yarn doc
7774
name: Building documentation
7875
working-directory: sdk-dist/typescript
7976

8077
build-react-ui-npm:
81-
needs: [build, test]
78+
needs: [ build, test ]
8279
runs-on: ubuntu-latest
8380
steps:
8481
- uses: actions/checkout@v2
@@ -94,42 +91,42 @@ jobs:
9491
working-directory: sdk-dist/etd-react-ui
9592

9693
build-python:
97-
needs: [build, test]
94+
needs: [ build, test ]
9895
runs-on: ubuntu-latest
9996
steps:
100-
- uses: actions/checkout@master
101-
- name: Set up Python 3.9
102-
uses: actions/setup-python@v1
103-
with:
104-
python-version: 3.9
105-
- name: Install Pep8
106-
run: pip install -r requirements.txt
107-
working-directory: sdk-dist/python
108-
- name: Testing
109-
run: pytest
110-
working-directory: sdk-dist/python
111-
- name: Install pypa/build
112-
run: >-
113-
python -m
114-
pip install
115-
build
116-
--user
117-
working-directory: sdk-dist/python
118-
- name: Build a binary wheel and a source tarball
119-
run: >-
120-
python -m
121-
build
122-
--sdist
123-
--wheel
124-
--outdir dist/
125-
.
126-
working-directory: sdk-dist/python
97+
- uses: actions/checkout@master
98+
- name: Set up Python 3.9
99+
uses: actions/setup-python@v1
100+
with:
101+
python-version: 3.9
102+
- name: Install Pep8
103+
run: pip install -r requirements.txt
104+
working-directory: sdk-dist/python
105+
- name: Testing
106+
run: pytest
107+
working-directory: sdk-dist/python
108+
- name: Install pypa/build
109+
run: >-
110+
python -m
111+
pip install
112+
build
113+
--user
114+
working-directory: sdk-dist/python
115+
- name: Build a binary wheel and a source tarball
116+
run: >-
117+
python -m
118+
build
119+
--sdist
120+
--wheel
121+
--outdir dist/
122+
.
123+
working-directory: sdk-dist/python
127124

128125

129126
release:
130127
runs-on: ubuntu-latest
131128
if: ${{ (github.event.pusher.name != 'github action') && (github.ref == 'refs/heads/main') }}
132-
needs: [build-kotlin, build-react-ui-npm, build-typescript-npm, build-python]
129+
needs: [ build-kotlin, build-react-ui-npm, build-typescript-npm, build-python ]
133130
steps:
134131
- name: Checkout 🛎️
135132
uses: actions/checkout@v2

.gitignore

+2
Original file line numberDiff line numberDiff line change
@@ -12,3 +12,5 @@ docs/kotlin/*.md
1212
*.egg-info
1313
__pycache__
1414
sdk-dist/typescript/yarn-error.log
15+
coverage
16+
*.tsbuildinfo

sdk-dist/typescript/.gitignore

+1
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
11
.turbo
22
dist
33
docs
4+
*.jpg
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,147 @@
1+
import {
2+
ChainId,
3+
SignedTransaction,
4+
Transaction,
5+
} from "@etherdata-blockchain/etherdata-sdk-common";
6+
import utils from "web3-utils";
7+
import { TransactionFactory } from "@ethereumjs/tx";
8+
import elliptic from "elliptic";
9+
import { Wallet } from "@ethersproject/wallet";
10+
import { keccak256 } from "@ethersproject/keccak256";
11+
import { getAddress, getIcapAddress } from "@ethersproject/address";
12+
13+
/**
14+
* Create an account object which can be used for signing
15+
*/
16+
export class Account {
17+
privateKey: string;
18+
address: string;
19+
20+
constructor(privateKey: string) {
21+
const secp256k1 = new elliptic.ec("secp256k1");
22+
23+
const buffer = new Buffer(privateKey.slice(2), "hex");
24+
const ecKey = secp256k1.keyFromPrivate(buffer);
25+
const publicKey = "0x" + ecKey.getPublic(false, "hex").slice(2);
26+
const publicHash = keccak256(publicKey);
27+
this.address = this.toChecksum("0x" + publicHash.slice(-40));
28+
this.privateKey = privateKey;
29+
}
30+
31+
static fromMnemonic(mnemonic: string): Account {
32+
const privateKey = Wallet.fromMnemonic(mnemonic).privateKey;
33+
return new Account(privateKey);
34+
}
35+
36+
static randomCreate() {
37+
const privateKey = Wallet.createRandom().privateKey;
38+
return new Account(privateKey);
39+
}
40+
41+
/**
42+
* Sign transaction. Will automatically use current wallet address to as the from field.
43+
* @param tx
44+
*/
45+
signTransaction(tx: Transaction): SignedTransaction {
46+
if (tx.chainId === undefined) {
47+
tx.chainId = ChainId.MainNet;
48+
}
49+
let privateKey = this.privateKey;
50+
this.validateTransactionForSigning(tx);
51+
52+
tx.from = this.addressFormatter(tx.from ?? this.address);
53+
tx.data = tx.data || "0x";
54+
tx.value = this.toHex(tx.value);
55+
tx.gasLimit = this.toHex(tx.gasLimit || tx.gas);
56+
tx.gas = this.toHex(tx.gas);
57+
tx.type = this.toHex(tx.type ?? 0);
58+
tx.nonce = this.toHex(tx.nonce);
59+
tx.chainId = this.toHex(tx.chainId);
60+
61+
if (tx.type === "0x1" && tx.accessList === undefined) tx.accessList = [];
62+
63+
if (privateKey.startsWith("0x")) {
64+
privateKey = privateKey.substring(2);
65+
}
66+
let newTx = TransactionFactory.fromTxData(tx);
67+
let signedTx = newTx.sign(Buffer.from(privateKey, "hex"));
68+
let validationErrors = signedTx.validate(true);
69+
if (validationErrors.length > 0) {
70+
let errorString = "Signer Error: ";
71+
for (const validationError of validationErrors) {
72+
errorString += `${errorString} ${validationError}.`;
73+
}
74+
throw new Error(errorString);
75+
}
76+
let rlpEncoded = signedTx.serialize().toString("hex");
77+
let rawTransaction = "0x" + rlpEncoded;
78+
let transactionHash = utils.keccak256(rawTransaction);
79+
return {
80+
messageHash:
81+
"0x" + Buffer.from(signedTx.getMessageToSign(true)).toString("hex"),
82+
v: "0x" + signedTx.v!.toString(16),
83+
r: "0x" + signedTx.r!.toString(16),
84+
s: "0x" + signedTx.s!.toString(16),
85+
rawTransaction: rawTransaction,
86+
transactionHash: transactionHash,
87+
};
88+
}
89+
90+
private toChecksum(address: string) {
91+
return getAddress(address);
92+
}
93+
94+
private toHex(value: string | number) {
95+
let newValue = value;
96+
// handle base16 value or base10's value in string format
97+
if (typeof newValue === "string") {
98+
if (!newValue.startsWith("0x")) {
99+
newValue = parseInt(newValue).toString(16);
100+
}
101+
}
102+
103+
// handle base 10's value
104+
if (typeof newValue === "number") {
105+
newValue = newValue.toString(16);
106+
}
107+
108+
if (!newValue.startsWith("0x")) {
109+
newValue = `0x${newValue}`;
110+
}
111+
112+
return newValue;
113+
}
114+
115+
private addressFormatter(address: string) {
116+
return getIcapAddress(address);
117+
}
118+
119+
private validateTransactionForSigning(tx: Transaction) {
120+
if (
121+
!tx.gas &&
122+
!tx.gasLimit &&
123+
!tx.maxPriorityFeePerGas &&
124+
!tx.maxFeePerGas
125+
) {
126+
throw new Error('"gas" is missing');
127+
}
128+
129+
if (tx.chainId === undefined) {
130+
throw new Error('"chainId" is missing');
131+
}
132+
133+
if (tx.gas && tx.gasPrice) {
134+
if (tx.gas < 0 || tx.gasPrice < 0) {
135+
throw new Error("Gas or gasPrice is lower than 0");
136+
}
137+
} else {
138+
if ((tx.maxPriorityFeePerGas ?? 0) < 0 || (tx.maxFeePerGas ?? 0) < 0) {
139+
throw new Error("maxPriorityFeePerGas or maxFeePerGas is lower than 0");
140+
}
141+
}
142+
if (tx.nonce < 0 || tx.chainId < 0) {
143+
throw new Error("Nonce or chainId is lower than 0");
144+
}
145+
return;
146+
}
147+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
export * from "./account";

sdk-dist/typescript/packages/account/package.json

+8-1
Original file line numberDiff line numberDiff line change
@@ -2,10 +2,17 @@
22
"name": "@etherdata-blockchain/etherdata-sdk-account",
33
"version": "4.0.3",
44
"main": "dist/index.js",
5+
"types": "dist/index.d.ts",
56
"license": "MIT",
67
"private": false,
78
"dependencies": {
8-
9+
"web3-utils": "^1.7.3",
10+
"@ethereumjs/tx": "^3.5.1",
11+
"elliptic": "^6.5.4",
12+
"@types/elliptic" : "^6.4.14",
13+
"@ethersproject/wallet": "^5.6.0",
14+
"@ethersproject/address": "^5.6.0",
15+
"@etherdata-blockchain/etherdata-sdk-common": "*"
916
},
1017
"repository": {
1118
"type": "git",

0 commit comments

Comments
 (0)