Skip to content

Commit

Permalink
resolve review comments
Browse files Browse the repository at this point in the history
  • Loading branch information
KwilLuke committed Feb 3, 2025
1 parent 65fb45a commit 352fe1c
Show file tree
Hide file tree
Showing 12 changed files with 90 additions and 88 deletions.
3 changes: 1 addition & 2 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -24,8 +24,7 @@
"pack_pre": "copyfiles package.json README.md dist && rimraf ./kwil*.tgz",
"pack_post": "copyfiles ./kwil*.tgz ./pkg && rimraf ./kwil*.tgz",
"pack": "npm run pack_build && npm run pack_pre && npm --prefix ./dist pack && npm run pack_post",
"integration": "jest --runInBand --testPathPattern=tests/integration --testTimeout=30000",
"scratchpad": "ts-node testing-functions/scratchpad.js"
"integration": "jest --runInBand --testPathPattern=tests/integration --testTimeout=30000"
},
"author": "Kwil, Inc. <luke@kwil.com>",
"license": "MIT",
Expand Down
21 changes: 15 additions & 6 deletions src/client/kwil.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,6 @@ import {
EnvironmentType,
PayloadType,
} from '../core/enums';

import { getAccountId } from '../utils/keys';
import { ActionBody, CallBody, Entries, transformActionInput } from '../core/action';
import { KwilSigner } from '../core/kwilSigner';
import { wrap } from './intern';
Expand All @@ -29,6 +27,8 @@ import { Base64String, QueryParams } from '../utils/types';
import { PayloadTx } from '../transaction/payloadTx';
import { RawStatementPayload } from '../core/payload';
import { resolveNamespace, validateNamespace } from '../utils/namespace';
import { inferKeyType } from '../utils/keys';
import { bytesToHex } from '../utils/serial';

/**
* The main class for interacting with the Kwil network.
Expand Down Expand Up @@ -94,13 +94,22 @@ export abstract class Kwil<T extends EnvironmentType> extends Client {
/**
* Retrieves an account using the owner's Ethereum wallet address.
*
* @param owner - The owner's identifier (e.g. Ethereum wallet address or NEAR public key). Ethereum addresses can be passed as a hex string (0x123...) or as bytes (Uint8Array). NEAR protocol public keys can be passed as the base58 encoded public key (with "ed25519:" prefix), a hex string, or bytes (Uint8Array).
* @param owner - The owner's identifier (e.g. Ethereum wallet address or ED25119 keys). Ethereum addresses and ED25519 public keys can be passed as a hex string (0x123...) or as bytes (Uint8Array).
* @returns A promise that resolves to an Account object. The account object includes the account's id, balance, and nonce.
*/
public async getAccount(owner: string | Uint8Array): Promise<GenericResponse<Account>> {
const accountId = getAccountId(owner);
public async getAccount(owner: string | Uint8Array, keyType?: string): Promise<GenericResponse<Account>> {
if (!keyType) {
keyType = inferKeyType(owner);
}

return await this.getAccountClient(accountId);
if(owner instanceof Uint8Array) {
owner = bytesToHex(owner);
}

return await this.getAccountClient({
identifier: owner,
key_type: keyType,
});
}

/**
Expand Down
1 change: 0 additions & 1 deletion src/core/network.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@
import { HexString } from '../utils/types';
import { AccountKeyType, BytesEncodingStatus } from './enums';

// TODO: Support for other key types / string
export interface AccountId {
identifier: string;
// can be a built in key type, or a string if using a custom signer
Expand Down
18 changes: 14 additions & 4 deletions src/funder/funder.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { Kwil } from '../client/kwil';
import {
AccountKeyType,
BroadcastSyncType,
EnvironmentType,
PayloadType,
Expand All @@ -10,7 +11,7 @@ import { GenericResponse } from '../core/resreq';
import { Transaction, TxReceipt } from '../core/tx';
import { TransferBody } from './funding_types';
import { PayloadTx } from '../transaction/payloadTx';
import { getAccountId } from '../utils/keys';
import { bytesToHex } from '../utils/serial';

interface FunderClient {
broadcastClient(
Expand All @@ -35,16 +36,25 @@ export class Funder<T extends EnvironmentType> {
signer: KwilSigner,
synchronous?: boolean
): Promise<GenericResponse<TxReceipt>> {
const accountId = getAccountId(payload.to);
if (!payload.keyType) {
payload.keyType = AccountKeyType.SECP256K1
}

if (payload.to instanceof Uint8Array) {
payload.to = bytesToHex(payload.to);
}

const txPayload: TransferPayload = {
to: accountId,
to: {
identifier: payload.to,
key_type: payload.keyType,
},
amount: payload.amount.toString(),
};

const transaction = await PayloadTx.createTx(this.kwil, {
chainId: this.chainId,
description: payload.description!,
description: payload.description || '',
payload: txPayload,
payloadType: PayloadType.TRANSFER,
identifier: signer.identifier,
Expand Down
3 changes: 3 additions & 0 deletions src/funder/funding_types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,13 @@
* `TransferBody` is the body of a transfer request.
*
* @param to The address / identifier to transfer to. Can be a hex string or a Uint8Array.
* @param keyType The key type of the address. Defaults to 'secp256k1'.
* @param amount The amount to transfer.
* @param description A description of the transfer. Optional.
*/
export interface TransferBody {
to: string | Uint8Array;
keyType?: string;
amount: BigInt;
description?: string;
}
1 change: 0 additions & 1 deletion src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,6 @@ import { GenericResponse as _GenericResponse } from './core/resreq';
import { TxResult as _TxResult, TxInfoReceipt as _TxInfoReceipt } from './core/txQuery';
import { Account as _Account, DatasetInfo as _DatasetInfo } from './core/network';
import { MsgReceipt as _MsgReceipt, Message as _Message } from './core/message';
import { recoverSecp256k1PubKey as _recoverSecp256k1PubKey } from './utils/keys';
import { KwilSigner } from './core/kwilSigner';
import { PayloadType as _PayloadType } from './core/enums';
import Client from './api_client/client';
Expand Down
5 changes: 0 additions & 5 deletions src/utils/dbid.ts
Original file line number Diff line number Diff line change
@@ -1,14 +1,9 @@
import { concatBytes } from "./bytes";
import { sha224BytesToString } from "./crypto";
import { isNearPubKey, nearB58ToHex } from "./keys";
import { hexToBytes, stringToBytes } from "./serial";

export function generateDBID(owner: string | Uint8Array, name: string): string {
if(typeof owner === "string") {
if(isNearPubKey(owner)) {
owner = nearB58ToHex(owner);
}

owner = hexToBytes(owner);
}

Expand Down
67 changes: 6 additions & 61 deletions src/utils/keys.ts
Original file line number Diff line number Diff line change
@@ -1,73 +1,18 @@
import { SigningKey, hashMessage } from 'ethers';
import { EthSigner } from '../core/signature';
import { from_b58 } from './base58';
import { bytesToHex, hexToBytes, stringToBytes } from './serial';
import { isEthersSigner } from '../core/signature';
import { AccountId } from '../core/network';
import { hexToBytes } from './serial';
import { AccountKeyType } from '../core/enums';

// Public Keys
export function isNearPubKey(pubKey: string): boolean {
return pubKey.startsWith('ed25519:');
}

function trimNearPrefix(str: string): string {
return str.replace('ed25519:', '');
}

export function nearB58ToHex(b58: string): string {
b58 = trimNearPrefix(b58);
const b58Bytes = from_b58(b58);
if (!b58Bytes) {
throw new Error(`invalid base58 string: ${b58}`);
}
return bytesToHex(b58Bytes);
}

export function getAccountId(owner: string | Uint8Array): AccountId {
export function inferKeyType(owner: string | Uint8Array): AccountKeyType {
if (typeof owner === 'string') {
owner = hexToBytes(owner);
}

if (owner.length === 32) {
return {
key_type: AccountKeyType.ED25519,
identifier: bytesToHex(owner),
}
return AccountKeyType.ED25519;
}

if (owner.length === 20) {
return {
key_type: AccountKeyType.SECP256K1,
identifier: bytesToHex(owner),
}
return AccountKeyType.SECP256K1;
}

throw new Error('Invalid owner length');
}

/**
* Recover a secp256k1 public key from a signer. This will force the user to sign a message.
*
* @param signer - An ethereum signer from Ethers v5 or Ethers v6.
* @param message - The message to sign. Defaults to 'Sign this message to recover your public key.'.
* @returns A promise that resolves to the recovered public key.
* @deprecated No longer supported. Ethereum accounts are now identified by their address. (will be removed in v0.6.0)
*/

export async function recoverSecp256k1PubKey(
signer: EthSigner,
message: string = 'Sign this message to recover your public key.'
): Promise<string> {
if (isEthersSigner(signer)) {
const signature = await signer.signMessage(message);
return ecrRecoverPubKey(stringToBytes(message), signature);
}

throw new Error('Signer must be an ethereum signer from Ethers v5 or Ethers v6.');
}

export function ecrRecoverPubKey(unsignedMessage: Uint8Array, signature: string): string {
const msgHash = hashMessage(unsignedMessage);
return SigningKey.recoverPublicKey(msgHash, signature);
}
throw new Error('Cannot determine key type from owner.');
}
5 changes: 2 additions & 3 deletions src/utils/parameterEncoding.ts
Original file line number Diff line number Diff line change
Expand Up @@ -86,12 +86,11 @@ function formatDataType(val: ValueType | ValueType[]): {
data: ValueType;
} {
const { metadata, varType } = resolveValueType(val);
const metadataSpread = metadata ? { metadata } : {};

const dataType: DataType = {
name: varType,
is_array: Array.isArray(val),
...metadataSpread,
metadata,
};

return { type: dataType, data: val };
Expand Down Expand Up @@ -121,7 +120,7 @@ export function analyzeNumber(num: number) {
}

export function resolveValueType(value: ValueType | ValueType[]): {
metadata: [number, number] | undefined;
metadata: [number, number];
varType: VarType;
} {
if (Array.isArray(value)) {
Expand Down
6 changes: 3 additions & 3 deletions testing-functions/scratchpad.js → testing-functions/test.js
Original file line number Diff line number Diff line change
Expand Up @@ -91,7 +91,7 @@ async function scratchpad() {
// await julioSignature(kwil, dbid)
// await customEd25519(kwil, dbid)
// await dropDb(kwil, kwilSigner, dbid);
// await transfer(kwil, "0x7e5f4552091a69125d5dfcb7b8c2659029395bdf", 20, kwilSigner)
await transfer(kwil, "0x7e5f4552091a69125d5dfcb7b8c2659029395bdf", 20, kwilSigner)
// bulkActionInput(kwil, kwilSigner)
// console.log(base64ToBytes('QVFJREJBVUdCd2dKQ2c9PQ=='))

Expand All @@ -109,7 +109,7 @@ async function scratchpad() {
$id: 1,
$message: "hello_world"
};
executeGeneralAction(kwil, dbid, 'insert_greeting', kwilSigner, post);
// executeGeneralAction(kwil, dbid, 'insert_greeting', kwilSigner, post);
// await executeGeneralView(kwil, dbid, 'get_profile', null, kwilSigner);
// await executeGeneralView(kwil, dbid, "view_must_sign", null, kwilSigner)
}
Expand Down Expand Up @@ -492,7 +492,7 @@ async function customEd25519(kwil, dbid) {
async function transfer(kwil, to, tokenAmnt, signer) {
const payload = {
to,
amount: BigInt(tokenAmnt * 10 ** 18),
amount: BigInt(tokenAmnt),
};

const res = await kwil.funder.transfer(payload, signer);
Expand Down
46 changes: 46 additions & 0 deletions tests/integration/funder.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
import { TransferBody } from '../../src/funder/funding_types';
import { bytesToHex } from '../../src/utils/serial';
import { kwil, isGasOn, kwilSigner, deriveKeyPair64 } from './setup';

// testing balance transfer / management
(isGasOn ? describe : describe.skip)('Funder', () => {
it('should transfer funds to Eth Accounts', async () => {
const transferBody: TransferBody = {
to: '0x6E2fA2aF9B4eF5c8A3BcF9A9B9A4F1a1a2c1c1c1',
amount: BigInt(1),
}

const result = await kwil.funder.transfer(transferBody, kwilSigner, true);

expect(result.data).toMatchObject({
tx_hash: expect.any(String),
})
})

it('should transfer funds to ED25119 accounts', async () => {
const edKeys = await deriveKeyPair64('69420', '69420');
const edPk = bytesToHex(edKeys.publicKey);

const transferBody: TransferBody = {
to: edPk,
amount: BigInt(1),
}

const result = await kwil.funder.transfer(transferBody, kwilSigner, true);

expect(result.data).toMatchObject({
tx_hash: expect.any(String),
})
})

describe('Error Cases', () => {
it('should fail when using a custom key type without specification', async () => {
const transferBody: TransferBody = {
to: 'zzzzz',
amount: BigInt(1),
}

await expect(kwil.funder.transfer(transferBody, kwilSigner, true)).rejects.toThrow();
})
})
})
2 changes: 0 additions & 2 deletions tests/integration/signers.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,6 @@ import { bytesToHex } from '../../src/utils/serial';
import { GenericResponse } from '../../src/core/resreq';
import { Account } from '../../src/core/network';

// TODO: This needs to be updated to allow for a signer other than the db owner to create DBs
// These tests will only work if the edSigner has permissions to deploy the schema
describe('Testing custom signers', () => {
let edSigner: KwilSigner;
let input: Types.ActionInput;
Expand Down

0 comments on commit 352fe1c

Please sign in to comment.