Skip to content

Commit

Permalink
Merge pull request #166 from thepower/feat/eth-transactions
Browse files Browse the repository at this point in the history
Feat/eth transactions
  • Loading branch information
jackkru69 authored Oct 22, 2024
2 parents d916599 + aca4660 commit f99c9ab
Show file tree
Hide file tree
Showing 21 changed files with 472 additions and 244 deletions.
3 changes: 2 additions & 1 deletion packages/cli/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,8 @@
"enquirer": "^2.4.1",
"json-colorizer": "^3.0.1",
"jsonwebtoken": "^9.0.2",
"listr2": "^8.2.1"
"listr2": "^8.2.1",
"viem": "^2.19.6"
},
"devDependencies": {
"@oclif/test": "^4",
Expand Down
12 changes: 9 additions & 3 deletions packages/cli/src/commands/acc/get-balance.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,32 +20,38 @@ export default class AccGetBalance extends BaseCommand {
address: Flags.string({
aliases: ['adr'], char: 'a', description: 'Wallet address', exclusive: ['keyFilePath'],
}),
chain: Flags.integer({
char: 'c', description: 'Chain ID',
}),
bootstrapChain: Flags.integer({ char: 'b', default: 1025, description: 'Bootstrap chain ID' }),
keyFilePath: Flags.file({ char: 'k', description: 'Path to the key file', exclusive: ['address'] }),
password: Flags.string({
char: 'p', default: '', description: 'Password for the key file (env: KEY_FILE_PASSWORD)', env: 'KEY_FILE_PASSWORD',
}),
isEth: Flags.boolean({
char: 'e', description: 'Get balance of an Ethereum account', exclusive: ['address'], default: false,
}),
};

public async run(): Promise<void> {
const { flags } = await this.parse(AccGetBalance);
const {
address, bootstrapChain, keyFilePath, password,
address, chain, bootstrapChain, keyFilePath, password, isEth,
} = flags;

ux.action.start('Loading');

let walletAddress = address;
if (keyFilePath) {
const importedWallet = await loadWallet(keyFilePath, password);
const importedWallet = await loadWallet(keyFilePath, password, isEth);
walletAddress = importedWallet.address;
}

if (!walletAddress) {
throw new Error('No wallet address provided.');
}

const networkApi = await initializeNetworkApi({ address: walletAddress, defaultChain: bootstrapChain });
const networkApi = await initializeNetworkApi({ address: walletAddress, chain, defaultChain: bootstrapChain });

const wallet = new WalletApi(networkApi);
const result = await wallet.loadBalance(walletAddress);
Expand Down
84 changes: 66 additions & 18 deletions packages/cli/src/commands/acc/register.ts
Original file line number Diff line number Diff line change
@@ -1,27 +1,20 @@
import { color } from '@oclif/color';
import { Flags, ux } from '@oclif/core';
import { NetworkApi, NetworkEnum, WalletApi } from '@thepowereco/tssdk';
import {
CryptoApi, NetworkApi, NetworkEnum, WalletApi,
} from '@thepowereco/tssdk';
import { prompt } from 'enquirer';
import { colorize } from 'json-colorizer';
import { writeFileSync } from 'node:fs';
import * as path from 'node:path';

import { publicKeyToAddress } from 'viem/utils';
import { BaseCommand } from '../../baseCommand';

interface AccountData {
address: string
chain: number
wif: string
}

interface RegisterFlags {
chain?: number
filePath?: string
hint?: string
network?: string
noSave?: boolean
password?: string
referrer?: string
seed?: string
wif: string
}

const networks = [NetworkEnum.devnet, NetworkEnum.testnet, NetworkEnum.appchain];
Expand Down Expand Up @@ -51,15 +44,25 @@ Register a new account on a specified chain without saving the data to a file.`,
password: Flags.string({ char: 'p', default: '', description: 'Password for the account' }),
referrer: Flags.string({ char: 'r', description: 'Referrer for the account' }),
seed: Flags.string({ char: 's', description: 'Seed for the account' }),
isEth: Flags.boolean({
char: 'e', description: 'Register an Ethereum account',
}),
hdPath: Flags.string({
char: 'd', description: 'HD path for the account', default: 'm/44\'/60\'/0\'',
}),
};

public async run(): Promise<void> {
const { flags } = await this.parse(AccRegister);
const {
chain, filePath, hint, network, noSave, password, referrer, seed,
}: RegisterFlags = flags;
chain, filePath, hint, network, noSave, password, referrer, seed, isEth, hdPath,
} = flags;

if (chain) {
if (isEth) {
await this.registerEthAccount({
filePath, hint, noSave, password, seed, hdPath: hdPath as `m/44'/60'/${string}`,
});
} else if (chain) {
await this.registerAccountOnChain({
chain, filePath, hint, noSave, password, referrer, seed,
});
Expand All @@ -81,11 +84,12 @@ Register a new account on a specified chain without saving the data to a file.`,
hint?: string
noSave?: boolean
password: string
isEth?: boolean
}) {
const {
acc, defaultFileName, filePath, hint, noSave, password,
acc, defaultFileName, filePath, hint, noSave, password, isEth,
} = params;
const exportedData = WalletApi.getExportData(acc.wif, acc.address, password, hint);
const exportedData = WalletApi.getExportData(acc.wif, acc.address, password, hint, isEth);
this.log(colorize(acc));
if (!noSave) {
const savePath = this.getSavePath(filePath, defaultFileName);
Expand Down Expand Up @@ -139,6 +143,7 @@ Register a new account on a specified chain without saving the data to a file.`,

ux.action.start('Loading');
const acc = await WalletApi.registerCertainChain({ chain, customSeed: seed, referrer });

ux.action.stop();

await this.exportAndSaveAccountData({
Expand All @@ -155,6 +160,48 @@ Register a new account on a specified chain without saving the data to a file.`,
}
}

private async registerEthAccount(params: {
filePath?: string
hint?: string
noSave?: boolean
password: string
seed?: string
hdPath: string
}) {
const {
filePath, hint, noSave, password, seed, hdPath,
} = params;

ux.action.start('Ethereum account registration');
const seedPhrase = seed || CryptoApi.generateSeedPhrase();

const keyPair = await CryptoApi.generateKeyPairFromSeedPhrase(
seedPhrase,
hdPath,
);

const address = publicKeyToAddress(`0x${keyPair.publicKey.toString('hex')}`);
const wif = keyPair.toWIF();

if (!wif) {
throw new Error('Failed to generate private key');
}

ux.action.stop();

await this.exportAndSaveAccountData({
acc: { address, wif },
defaultFileName: `eth_wallet_${address}.pem`,
filePath,
hint,
noSave,
password,
isEth: true,
});

this.log(color.greenBright('Ethereum account successfully created!'));
}

private async registerAccountOnChain(params: {
chain: number
filePath?: string
Expand All @@ -169,6 +216,7 @@ Register a new account on a specified chain without saving the data to a file.`,
} = params;
ux.action.start('Account registration');
const acc = await WalletApi.registerCertainChain({ chain, customSeed: seed, referrer });

ux.action.stop();

await this.exportAndSaveAccountData({
Expand Down
12 changes: 9 additions & 3 deletions packages/cli/src/commands/acc/send-sk.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import { WalletApi } from '@thepowereco/tssdk';
import { colorize } from 'json-colorizer';

import color from '@oclif/color';
import { parseUnits } from 'viem/utils';
import { initializeNetworkApi, loadWallet } from '../../helpers/network.helper';
import { BaseCommand } from '../../baseCommand';
import cliConfig from '../../config/cli';
Expand All @@ -17,7 +18,7 @@ export default class AccSendSk extends BaseCommand {
];

static override flags = {
amount: Flags.integer({ char: 'a', description: 'Amount to send', required: true }),
amount: Flags.string({ char: 'a', description: 'Amount to send', required: true }),
bootstrapChain: Flags.integer({ char: 'b', default: 1025, description: 'Default chain ID' }),
keyFilePath: Flags.file({ char: 'k', description: 'Path to the key file', required: true }),
message: Flags.string({ char: 'm', default: '', description: 'Message to include' }),
Expand All @@ -31,6 +32,10 @@ export default class AccSendSk extends BaseCommand {
}),
gasToken: Flags.string({ char: 'g', description: 'Token used to pay for gas' }),
gasValue: Flags.integer({ char: 'v', description: 'Gas value for deployment' }),
chain: Flags.integer({
char: 'c',
description: 'Chain ID',
}),
};

public async run(): Promise<void> {
Expand All @@ -46,12 +51,13 @@ export default class AccSendSk extends BaseCommand {
decimals,
gasToken,
gasValue,
chain,
} = flags;

ux.action.start('Loading');

const importedWallet = await loadWallet(keyFilePath, password);
const networkApi = await initializeNetworkApi({ address: importedWallet.address, defaultChain: bootstrapChain });
const networkApi = await initializeNetworkApi({ address: importedWallet.address, defaultChain: bootstrapChain, chain });

if (!networkApi) {
throw new Error('No network found.');
Expand All @@ -65,7 +71,7 @@ export default class AccSendSk extends BaseCommand {
from: importedWallet.address,
to,
token,
inputAmount: BigInt(amount) * BigInt(10 ** decimals),
inputAmount: parseUnits(amount, decimals),
message,
gasToken,
gasValue: gasValue ? BigInt(gasValue) : undefined,
Expand Down
20 changes: 11 additions & 9 deletions packages/cli/src/commands/container/create.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import path from 'node:path';
import { AddressApi, EvmContract } from '@thepowereco/tssdk';
import color from '@oclif/color';
import { prompt } from 'enquirer';
import { isAddress } from 'viem/utils';
import { initializeNetworkApi, loadWallet } from '../../helpers/network.helper';
import cliConfig from '../../config/cli';
import abis from '../../abis';
Expand Down Expand Up @@ -37,18 +38,23 @@ export default class ContainerCreate extends BaseCommand {
sponsorAddress: Flags.string({
char: 'r', description: 'Address of the sponsor',
}),
chain: Flags.integer({
char: 'c',
description: 'Chain ID',
}),
};

public async run(): Promise<void> {
const { flags } = await this.parse(ContainerCreate);
const {
keyFilePath, password, containerKeyFilePath, containerName, containerPassword, ordersScAddress, sponsorAddress,
keyFilePath, password, containerKeyFilePath, containerName, containerPassword, ordersScAddress, sponsorAddress, chain,
} = flags;

ux.action.start('Loading');

const importedWallet = await loadWallet(keyFilePath, password);
const networkApi = await this.initializeNetwork(importedWallet.address);
const networkApi = await initializeNetworkApi({ address: importedWallet.address, chain });

const ordersContract = new EvmContract(networkApi, ordersScAddress);

const { privateKeyPem, publicKeyPem } = containerKeyFilePath
Expand All @@ -66,7 +72,9 @@ export default class ContainerCreate extends BaseCommand {
abi: abis.order,
functionName: 'mint',
args: [
AddressApi.textAddressToEvmAddress(importedWallet.address),
isAddress(importedWallet.address) ?
importedWallet.address :
AddressApi.textAddressToEvmAddress(importedWallet.address),
`0x${compactPublicKey?.buffer.toString('hex')}`,
stringToBytes32(containerName),
],
Expand All @@ -82,12 +90,6 @@ export default class ContainerCreate extends BaseCommand {
}
}

private async initializeNetwork(address: string) {
const networkApi = await initializeNetworkApi({ address });

return networkApi;
}

private async loadContainerKeys(containerKeyFilePath: string, containerPassword: string) {
const containerKeyFile = readFileSync(containerKeyFilePath, 'utf8');

Expand Down
28 changes: 24 additions & 4 deletions packages/cli/src/commands/container/list.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import { Flags, ux } from '@oclif/core';
import { AddressApi, EvmContract } from '@thepowereco/tssdk';
import Table from 'cli-table3';
import color from '@oclif/color';
import { isAddress } from 'viem/utils';
import { initializeNetworkApi, loadWallet } from '../../helpers/network.helper';
import cliConfig from '../../config/cli';
import abis from '../../abis';
Expand All @@ -26,23 +27,37 @@ export default class ContainerList extends BaseCommand {
ordersScAddress: Flags.string({
char: 'a', default: cliConfig.ordersScAddress, description: 'Orders smart contract address',
}),
chain: Flags.integer({
char: 'c',
description: 'Chain ID',
}),
};

public async run(): Promise<void> {
const { flags } = await this.parse(ContainerList);
const { keyFilePath, password, ordersScAddress } = flags;
const {
keyFilePath, password, ordersScAddress, chain,
} = flags;

ux.action.start('Loading');

// Initialize network API and wallet
const importedWallet = await loadWallet(keyFilePath, password);
const networkApi = await initializeNetworkApi({ address: importedWallet.address });
const networkApi = await initializeNetworkApi({ address: importedWallet.address, chain });

// Initialize EVM core and orders contract
const ordersContract = new EvmContract(networkApi, ordersScAddress);

const containersCount = await ordersContract.scGet(
{ abi: abis.order, functionName: 'balanceOf', args: [AddressApi.textAddressToEvmAddress(importedWallet.address)] },
{
abi: abis.order,
functionName: 'balanceOf',
args: [
isAddress(importedWallet.address) ?
importedWallet.address :
AddressApi.textAddressToEvmAddress(importedWallet.address),
],
},
);

// Fetch all containers owned by the user
Expand All @@ -51,7 +66,12 @@ export default class ContainerList extends BaseCommand {
const tokenId = await ordersContract.scGet({
abi: abis.order,
functionName: 'tokenOfOwnerByIndex',
args: [AddressApi.textAddressToEvmAddress(importedWallet.address), BigInt(index)],
args: [
isAddress(importedWallet.address) ?
importedWallet.address :
AddressApi.textAddressToEvmAddress(importedWallet.address),
BigInt(index),
],
});
return ordersContract.scGet({ abi: abis.order, functionName: 'tasks', args: [tokenId] });
}),
Expand Down
6 changes: 6 additions & 0 deletions packages/cli/src/commands/container/upload.ts
Original file line number Diff line number Diff line change
Expand Up @@ -96,6 +96,10 @@ export default class ContainerUpload extends BaseCommand {
default: cliConfig.providerScAddress,
description: 'Provider smart contract address',
}),
chain: Flags.integer({
char: 'n',
description: 'Chain ID',
}),
};

public async run(): Promise<void> {
Expand All @@ -110,12 +114,14 @@ export default class ContainerUpload extends BaseCommand {
chooseProvider,
ordersScAddress,
providerScAddress,
chain,
} = flags;
const importedWallet = await loadWallet(keyFilePath, password);

// Initialize network API
const networkApi = await initializeNetworkApi({
address: importedWallet.address,
chain,
});

// Initialize EVM and contract
Expand Down
Loading

0 comments on commit f99c9ab

Please sign in to comment.