Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

CSUB-578: Properly parse floating point amounts #1150

Merged
merged 21 commits into from
Jun 22, 2023
Merged
Show file tree
Hide file tree
Changes from 15 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion creditcoin-js/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,8 @@ export * from './creditcoin-api';
export * from './types';
export * from './model';

export { providers, Wallet } from 'ethers';
export { providers, Wallet, FixedNumber, BigNumber } from 'ethers';
export { parseUnits } from 'ethers/lib/utils';
export { Guid } from 'js-guid';

export { ApiPromise, WsProvider, Keyring } from '@polkadot/api';
Expand Down
49 changes: 29 additions & 20 deletions scripts/cc-cli/src/commands/bond.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,16 @@ import {
getStashSeedFromEnvOrPrompt,
initKeyringPair,
} from "../utils/account";
import { Balance, getBalance, toMicrounits } from "../utils/balance";
import { bond, parseRewardDestination } from "../utils/bond";
import { promptContinue } from "../utils/promptContinue";
import {
Balance,
getBalance,
parseCTCString,
toCTCString,
checkAmount,
} from "../utils/balance";
import { BN } from "creditcoin-js";

export function makeBondCommand() {
const cmd = new Command("bond");
Expand All @@ -29,23 +36,16 @@ export function makeBondCommand() {
async function bondAction(options: OptionValues) {
const { api } = await newApi(options.url);

// If no controller error and exit
checkAddress(options.controller, api);

// If no amount error and exit
if (!options.amount || !parseInt(options.amount, 10)) {
console.log("Must specify amount to bond");
process.exit(1);
}

const stashSeed = await getStashSeedFromEnvOrPrompt();

// Check balance
const stashKeyring = initKeyringPair(stashSeed);
const stashAddress = stashKeyring.address;
const balance = await getBalance(stashAddress, api);
const amount = parseInt(options.amount, 10);
checkBalanceAgainstBondAmount(balance, amount);

const amount = parseCTCString(options.amount);

// Check inputs
checkAddress(options.controller, api);
checkAmount(amount);
await checkBalance(amount, api, stashAddress);

const rewardDestination = options.rewardDestination
? parseRewardDestination(options.rewardDestination)
Expand All @@ -54,7 +54,7 @@ async function bondAction(options: OptionValues) {
console.log("Creating bond transaction...");
console.log("Controller address:", options.controller);
console.log("Reward destination:", rewardDestination);
console.log("Amount:", parseInt(options.amount, 10));
console.log("Amount:", toCTCString(amount));

await promptContinue();

Expand All @@ -73,10 +73,19 @@ async function bondAction(options: OptionValues) {
process.exit(0);
}

function checkBalanceAgainstBondAmount(balance: Balance, amount: number) {
if (balance.free.sub(balance.miscFrozen).lt(toMicrounits(amount))) {
throw new Error(
`Insufficient funds to bond ${toMicrounits(amount).toString()}`
async function checkBalance(amount: BN, api: any, address: string) {
const balance = await getBalance(address, api);
checkBalanceAgainstBondAmount(balance, amount);
atodorov marked this conversation as resolved.
Show resolved Hide resolved
}

function checkBalanceAgainstBondAmount(balance: Balance, amount: BN) {
const available = balance.free.sub(balance.miscFrozen);
if (available.lt(amount)) {
console.error(
`Insufficient funds to bond ${toCTCString(amount)}, only ${toCTCString(
available
)} available`
);
process.exit(1);
}
}
8 changes: 3 additions & 5 deletions scripts/cc-cli/src/commands/send.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import {
initECDSAKeyringPairFromPK,
initKeyringPair,
} from "../utils/account";
import { toMicrounits } from "../utils/balance";
import { parseCTCString } from "../utils/balance";

export function makeSendCommand() {
const cmd = new Command("send");
Expand Down Expand Up @@ -36,7 +36,6 @@ async function sendAction(options: OptionValues) {
const hash = await sendFromSr25519(options, api);
console.log("Transfer transaction hash: " + hash.toHex());
}

pLabarta marked this conversation as resolved.
Show resolved Hide resolved
process.exit(0);
}

Expand All @@ -55,7 +54,7 @@ async function sendFromSr25519(options: OptionValues, api: ApiPromise) {
// Send transaction
const tx = api.tx.balances.transfer(
options.to,
toMicrounits(options.amount).toString()
parseCTCString(options.amount).toString()
pLabarta marked this conversation as resolved.
Show resolved Hide resolved
);
const hash = await tx.signAndSend(caller);
return hash;
Expand All @@ -65,12 +64,11 @@ async function sendFromECDSA(options: OptionValues, api: ApiPromise) {
// Build account
const callerSeed = await getCallerSeedFromEnvOrPrompt();
const caller = initECDSAKeyringPairFromPK(callerSeed);
console.log(caller.address);

// Send transaction
const tx = api.tx.balances.transfer(
options.to,
toMicrounits(options.amount).toString()
parseCTCString(options.amount).toString()
pLabarta marked this conversation as resolved.
Show resolved Hide resolved
);
const hash = await tx.signAndSend(caller);
return hash;
Expand Down
4 changes: 2 additions & 2 deletions scripts/cc-cli/src/commands/unbond.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import {
getControllerSeedFromEnvOrPrompt,
initKeyringPair,
} from "../utils/account";
import { toMicrounits } from "../utils/balance";
import { parseCTCString } from "../utils/balance";
import { getStatus, requireStatus } from "../utils/status";

export function makeUnbondCommand() {
Expand Down Expand Up @@ -35,7 +35,7 @@ async function unbondAction(options: OptionValues) {
requireStatus(stashStatus, "bonded");
atodorov marked this conversation as resolved.
Show resolved Hide resolved

// Unbond transaction
const tx = api.tx.staking.unbond(toMicrounits(options.amount).toString());
const tx = api.tx.staking.unbond(parseCTCString(options.amount).toString());
atodorov marked this conversation as resolved.
Show resolved Hide resolved
pLabarta marked this conversation as resolved.
Show resolved Hide resolved

const hash = await tx.signAndSend(controllerKeyring);
AdaJane marked this conversation as resolved.
Show resolved Hide resolved

Expand Down
39 changes: 19 additions & 20 deletions scripts/cc-cli/src/commands/wizard.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,10 @@ import {
import {
Balance,
getBalance,
parseCTCString,
printBalance,
toMicrounits,
toCTCString,
checkAmount,
} from "../utils/balance";
import { bond, parseRewardDestination } from "../utils/bond";
import { perbillFromPercent, percentFromPerbill } from "../utils/perbill";
Expand Down Expand Up @@ -49,9 +51,10 @@ export function makeWizardCommand() {
const controllerAddress = controllerKeyring.address;

// Bond prefs
const amount: string = options.amount
? parseInt(options.amount, 10).toString()
: "0";
const amount = parseCTCString(options.amount);

checkAmount(amount);

const rewardDestination = options.rewardDestination
? parseRewardDestination(options.rewardDestination)
: "Staked";
Expand All @@ -70,7 +73,7 @@ export function makeWizardCommand() {
console.log("Using the following parameters:");
console.log(`💰 Stash account: ${stashAddress}`);
console.log(`🕹️ Controller account: ${controllerAddress}`);
console.log(`🪙 Amount to bond: ${amount} CTC`);
console.log(`🪙 Amount to bond: ${toCTCString(amount)}`);
pLabarta marked this conversation as resolved.
Show resolved Hide resolved
console.log(`🎁 Reward destination: ${rewardDestination}`);
console.log(`📡 Node URL: ${nodeUrl}`);
console.log(`💸 Commission: ${percentFromPerbill(commission).toString()}`);
Expand All @@ -82,22 +85,22 @@ export function makeWizardCommand() {
// Check both accounts have funds
const stashBalance = await getBalance(stashAddress, api);
const controllerBalance = await getBalance(controllerAddress, api);
checkStashBalance(stashAddress, stashBalance, options.amount);
checkControllerBalance(controllerAddress, controllerBalance, 1);
const bondExtra: boolean = checkIfAlreadyBonded(stashAddress, stashBalance);
checkStashBalance(stashAddress, stashBalance, amount);
checkControllerBalance(controllerAddress, controllerBalance, new BN(2));
AdaJane marked this conversation as resolved.
Show resolved Hide resolved
const bondExtra: boolean = checkIfAlreadyBonded(stashBalance);

if (bondExtra) {
console.log(
"⚠️ Warning: Stash account already bonded. This will increase the amount bonded."
);
if (await promptContinueOrSkip(`Continue or skip bonding extra funds?`)) {
checkStashBalance(stashAddress, stashBalance, options.amount);
checkStashBalance(stashAddress, stashBalance, amount);
// Bond extra
console.log("Sending bond transaction...");
const bondTxHash = await bond(
stashSeed,
controllerAddress,
parseInt(options.amount, 10),
amount,
rewardDestination,
api,
bondExtra
Expand All @@ -110,7 +113,7 @@ export function makeWizardCommand() {
const bondTxHash = await bond(
stashSeed,
controllerAddress,
parseInt(options.amount, 10),
amount,
rewardDestination,
api
);
Expand Down Expand Up @@ -151,12 +154,8 @@ export function makeWizardCommand() {
return cmd;
}

function checkControllerBalance(
address: string,
balance: Balance,
amount: number
) {
if (balance.free < new BN(amount)) {
function checkControllerBalance(address: string, balance: Balance, amount: BN) {
if (balance.free.lt(amount)) {
console.log(
"Controller account does not have enough funds to pay transaction fees"
);
Expand All @@ -168,8 +167,8 @@ function checkControllerBalance(
}
}

function checkStashBalance(address: string, balance: Balance, amount: number) {
if (balance.free.sub(balance.miscFrozen).lt(toMicrounits(amount))) {
function checkStashBalance(address: string, balance: Balance, amount: BN) {
if (balance.free.sub(balance.miscFrozen).lt(amount)) {
console.log(
`Stash account does not have enough funds to bond ${amount.toString()} CTC`
);
Expand All @@ -179,7 +178,7 @@ function checkStashBalance(address: string, balance: Balance, amount: number) {
}
}

function checkIfAlreadyBonded(address: string, balance: Balance) {
function checkIfAlreadyBonded(balance: Balance) {
if (balance.miscFrozen.gt(new BN(0))) {
return true;
} else {
Expand Down
33 changes: 25 additions & 8 deletions scripts/cc-cli/src/utils/balance.ts
Original file line number Diff line number Diff line change
@@ -1,17 +1,22 @@
import { BN } from "creditcoin-js";
import { BN, parseUnits } from "creditcoin-js";

const MICROUNITS_PER_CTC = new BN("1000000000000000000");
export const MICROUNITS_PER_CTC = new BN("1000000000000000000");

export function parseCTCString(amount: string): BN {
return new BN(amount).mul(MICROUNITS_PER_CTC);
}

export function toMicrounits(amount: number | BN): BN {
return new BN(amount).mul(MICROUNITS_PER_CTC);
try {
const parsed = parseUnits(amount, 18);
return new BN(parsed.toString());
} catch (e) {
console.error(`Unable to parse CTC amount: ${amount}`);
process.exit(1);
}
atodorov marked this conversation as resolved.
Show resolved Hide resolved
pLabarta marked this conversation as resolved.
Show resolved Hide resolved
}

export function toCTCString(amount: BN): string {
return amount.div(MICROUNITS_PER_CTC).toString() + " CTC";
const CTC = amount.div(MICROUNITS_PER_CTC);
const remainder = amount.mod(MICROUNITS_PER_CTC);
const remainderString = remainder.toString().padStart(18, "0");
return `${CTC.toString()}.${remainderString} CTC`;
}

export function readAmount(amount: string): BN {
Expand Down Expand Up @@ -50,3 +55,15 @@ export function printBalance(balance: Balance) {
console.log("Misc Frozen:", toCTCString(balance.miscFrozen));
console.log("Fee Frozen:", toCTCString(balance.feeFrozen));
}

export function checkAmount(amount: BN) {
if (!amount) {
console.log("Must specify amount to bond");
process.exit(1);
} else {
if (amount.lt(new BN(1).mul(MICROUNITS_PER_CTC))) {
console.log("Bond amount must be at least 1 CTC");
process.exit(1);
}
}
}
10 changes: 6 additions & 4 deletions scripts/cc-cli/src/utils/bond.ts
Original file line number Diff line number Diff line change
@@ -1,18 +1,20 @@
import { ApiPromise } from "creditcoin-js";
import { ApiPromise, BN } from "creditcoin-js";
import { initKeyringPair } from "./account";
import { MICROUNITS_PER_CTC } from "./balance";

export async function bond(
stashSeed: string,
controllerAddress: string,
amount: number,
amount: BN,
rewardDestination: "Staked" | "Stash" | "Controller",
api: ApiPromise,
extra = false
) {
if (amount < 1) {
if (amount.lt(new BN(1).mul(MICROUNITS_PER_CTC))) {
throw new Error("Amount to bond must be at least 1");
}
const amountInMicroUnits = BigInt(amount) * BigInt(1000000000000000000); // Multiply by to convert to micro units

const amountInMicroUnits = amount;

let bondTx;

Expand Down
6 changes: 5 additions & 1 deletion scripts/cc-cli/src/utils/promptContinue.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,13 @@ export async function promptContinue() {
type: "confirm",
name: "confirm",
message: "Continue?",
initial: true,
initial: false,
pLabarta marked this conversation as resolved.
Show resolved Hide resolved
});

if (!promptResult.confirm) {
process.exit(0);
}

return promptResult.confirm;
}

Expand Down
4 changes: 4 additions & 0 deletions scripts/cc-cli/yarn.lock
Original file line number Diff line number Diff line change
Expand Up @@ -2020,7 +2020,11 @@ create-require@^1.1.0:

"creditcoin-js@file:../../creditcoin-js/creditcoin-js-v0.9.5.tgz":
version "0.9.5"
<<<<<<< HEAD
resolved "file:../../creditcoin-js/creditcoin-js-v0.9.5.tgz#9b51fa1b480b211209174db858b6261878364f9a"
=======
resolved "file:../../creditcoin-js/creditcoin-js-v0.9.5.tgz#699b5c8cf04475e6455b689a6794e2b73947dced"
>>>>>>> e179b919 (Replace CTC string parsing with FixedNumber implementation; Export)
pLabarta marked this conversation as resolved.
Show resolved Hide resolved
dependencies:
"@polkadot/api" "9.14.2"
ethers "^5.7.1"
Expand Down