@kaiachain/ethers-ext
Version:
ethers.js extension for kaia blockchain
311 lines (310 loc) • 12.4 kB
JavaScript
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.AccountStore = exports.Accounts = void 0;
const address_1 = require("@ethersproject/address");
const providers_1 = require("@ethersproject/providers");
const signing_key_1 = require("@ethersproject/signing-key");
const transactions_1 = require("@ethersproject/transactions");
const js_ext_core_1 = require("@kaiachain/js-ext-core");
const signer_js_1 = require("./signer.js");
function isSameAddress(a, b) {
return (0, address_1.getAddress)(a) == (0, address_1.getAddress)(b);
}
function isSamePrivateKey(a, b) {
return (0, transactions_1.computeAddress)(a) == (0, transactions_1.computeAddress)(b);
}
// Accounts is array of Wallet in ethers.js Ext
class Accounts {
constructor(provider, list) {
this.wallets = [];
for (let i = 0; i < list.length; i++) {
if (list[i] instanceof signer_js_1.Wallet) {
// @ts-ignore
this.wallets.push(list[i]);
}
else if (Array.isArray(list[i])) {
// @ts-ignore
if (list[i].length == 1) {
// @ts-ignore
this.add([list[i][0]], provider);
// @ts-ignore
}
else if (list[i].length == 2) {
// @ts-ignore
this.add([list[i][0], list[i][1]], provider);
}
else {
throw new Error("Input has to be the array of [address, privateKey] or [privateKey]");
}
}
else {
throw new Error("Input has to be Wallet, [address, privateKey], or [privateKey]");
}
}
}
async add(account, provider) {
let addr;
let priv;
if (account.length == 1) {
const signingKey = new signing_key_1.SigningKey(account[0]);
addr = (0, transactions_1.computeAddress)(signingKey.compressedPublicKey);
priv = account[0];
}
else if (account.length == 2 && account[1] != undefined) {
addr = account[0];
priv = account[1];
}
else {
throw new Error("Input has to be [address, privateKey] or [privateKey]");
}
for (let i = 0; i < this.wallets.length; i++) {
if (isSameAddress(await this.wallets[i].getAddress(), addr) &&
isSamePrivateKey(this.wallets[i].privateKey, priv)) {
return false;
}
}
this.wallets.push(new signer_js_1.Wallet(addr, priv, provider));
return true;
}
async remove(account) {
let addr;
let priv;
if (account.length == 1) {
const signingKey = new signing_key_1.SigningKey(account[0]);
addr = (0, transactions_1.computeAddress)(signingKey.compressedPublicKey);
priv = account[0];
}
else if (account.length == 2 && account[1] != undefined) {
addr = account[0];
priv = account[1];
}
else {
throw new Error("Input has to be [address, privateKey] or [privateKey]");
}
for (let i = 0; i < this.wallets.length; i++) {
if (isSameAddress(await this.wallets[i].getAddress(), addr) &&
// @ts-ignore
isSamePrivateKey(await this.wallets[i].privateKey, priv)) {
delete this.wallets[i];
this.wallets.splice(i, 1);
return true;
}
}
return false;
}
removeAll() {
if (this.wallets.length == 0) {
return;
}
for (let i = this.wallets.length - 1; i >= 0 && i < this.wallets.length; i--) {
delete this.wallets[i];
this.wallets.splice(i, 1);
}
}
accountByKey(privateKey) {
const ret = [];
for (let i = 0; i < this.wallets.length; i++) {
if (isSameAddress(this.wallets[i].privateKey, privateKey)) {
ret.push(this.wallets[i]);
}
}
return ret;
}
async accountByAddress(address) {
const ret = [];
for (let i = 0; i < this.wallets.length; i++) {
if (isSameAddress(await this.wallets[i].getAddress(), address)) {
ret.push(this.wallets[i]);
}
}
return ret;
}
}
exports.Accounts = Accounts;
class AccountStore {
constructor() {
this.signableKeyList = [];
}
async refresh(provider, list) {
this.provider = provider;
if (this.accounts != undefined) {
this.accounts.removeAll();
}
this.accounts = new Accounts(provider, list);
const wallets = this.accounts.wallets;
this.signableKeyList = [];
await this.updateSignableKeyList();
this.accountInfos = [];
let accInfo;
for (let i = 0; i < wallets.length; i++) {
if (this.provider instanceof providers_1.JsonRpcProvider) {
const addr = await wallets[i].getAddress();
if (this.hasAccountInfos(addr)) {
continue;
}
const klaytn_account = await this.provider.send("klay_getAccount", [
addr,
"latest",
]);
const klaytn_accountKey = klaytn_account.account.key;
accInfo = {
address: addr,
nonce: klaytn_account.account.nonce,
balance: klaytn_account.account.balance,
key: {},
};
if (klaytn_accountKey.keyType == 1) {
// AccountKeyLegacy
accInfo.key = {
type: 1,
key: {},
};
}
else if (klaytn_accountKey.keyType == 2) {
// AccountKeyPublic
accInfo.key = {
type: 2,
key: {
pubkey: this.getPubkeyInfo(klaytn_accountKey.key.x, klaytn_accountKey.key.y),
},
};
}
else if (klaytn_accountKey.keyType == 4) {
// AccountKeyWeightedMultiSig
accInfo.key = {
type: 4,
key: {
threshold: klaytn_accountKey.key.threshold,
keys: [],
},
};
for (let i = 0; i < klaytn_accountKey.key.keys.length; i++) {
// @ts-ignore
accInfo.key.key.keys.push({
weight: klaytn_accountKey.key.keys[i].weight,
pubkey: this.getPubkeyInfo(klaytn_accountKey.key.keys[i].key.x, klaytn_accountKey.key.keys[i].key.y),
});
}
}
else if (klaytn_accountKey.keyType == 5) {
// AccountKeyRoleBased
accInfo.key = {
type: 5,
key: {
RoleTransaction: {},
RoleAccountUpdate: {},
RoleFeePayer: {},
},
};
const roleKeys = [];
for (let i = 0; i < klaytn_accountKey.key.length; i++) {
if (klaytn_accountKey.key[i].keyType == 1) {
// AccountKeyLegacy in the role-based key
roleKeys.push({
type: 1,
key: {},
});
}
else if (klaytn_accountKey.key[i].keyType == 2) {
// AccountKeyPublic in the role-based key
roleKeys.push({
type: 2,
key: {
pubkey: this.getPubkeyInfo(klaytn_accountKey.key[i].key.x, klaytn_accountKey.key[i].key.y),
},
});
}
else if (klaytn_accountKey.key[i].keyType == 4) {
// AccountKeyWeightedMultiSig in the role-based key
const multiKeys = {
type: 4,
key: {
threshold: klaytn_accountKey.key[i].key.threshold,
keys: [],
},
};
// add mult-keys
for (let j = 0; j < klaytn_accountKey.key[i].key.keys.length; j++) {
// @ts-ignore
multiKeys.key.keys.push({
weight: klaytn_accountKey.key[i].key.keys[j].weight,
pubkey: this.getPubkeyInfo(klaytn_accountKey.key[i].key.keys[j].key.x, klaytn_accountKey.key[i].key.keys[j].key.y),
});
}
roleKeys.push(multiKeys);
}
}
// @ts-ignore
accInfo.key.key = {
RoleTransaction: roleKeys[0],
RoleAccountUpdate: roleKeys[1],
RoleFeePayer: roleKeys[2],
};
}
this.accountInfos.push(accInfo);
}
else {
throw new Error("Klaytn typed transaction can only be broadcasted to a Klaytn JSON-RPC server");
}
}
}
async updateSignableKeyList() {
let i;
for (i = 0; this.accounts != undefined && i < this.accounts.wallets.length; i++) {
let hashedKey = await this.accounts.wallets[i].getEtherAddress();
hashedKey = String(hashedKey).toLocaleLowerCase();
if (this.hasInSignableKeyList(hashedKey) == false) {
this.signableKeyList.push(hashedKey);
}
}
}
hasInSignableKeyList(address) {
const hashedKey = String(address).toLocaleLowerCase();
return this.signableKeyList.indexOf(hashedKey) != -1;
}
hasAccountInfos(address) {
let i;
for (i = 0; this.accountInfos != undefined && i < this.accountInfos.length; i++) {
if (isSameAddress(this.accountInfos[i].address, address)) {
return true;
}
}
return false;
}
getType(address) {
let i;
for (i = 0; this.accountInfos != undefined && i < this.accountInfos.length; i++) {
if (isSameAddress(this.accountInfos[i].address, address)) {
return this.accountInfos[i].key.type;
}
}
return null;
}
getAccountInfo(address) {
let i;
for (i = 0; this.accountInfos != undefined && i < this.accountInfos.length; i++) {
if (isSameAddress(this.accountInfos[i].address, address)) {
return this.accountInfos[i];
}
}
return null;
}
getAccountInfos() {
return this.accountInfos;
}
getPubkeyInfo(x, y) {
const zeroPadX = js_ext_core_1.HexStr.zeroPad(x, 32);
const zeroPadY = js_ext_core_1.HexStr.zeroPad(y, 32);
const stripedX = String(zeroPadX).substring(2);
const stripedY = String(zeroPadY).substring(2);
const compressedKey = (0, signing_key_1.computePublicKey)(js_ext_core_1.HexStr.concat("0x04" + stripedX + stripedY), true);
const hashedKey = (0, transactions_1.computeAddress)(compressedKey);
const hasPrivateKey = this.hasInSignableKeyList(hashedKey);
return {
compressed: compressedKey,
hashed: hashedKey,
hasPrivateKey: hasPrivateKey,
};
}
}
exports.AccountStore = AccountStore;