Skip to content

Commit

Permalink
feat: suggest to distribute tokens on cc-create if not enough (#1097)
Browse files Browse the repository at this point in the history
* feat: suggest to distribute tokens on cc-create if not enough

* use min 10 FLT
  • Loading branch information
shamsartem authored Jan 21, 2025
1 parent ad7b5e7 commit dfb59a5
Show file tree
Hide file tree
Showing 5 changed files with 118 additions and 92 deletions.
82 changes: 80 additions & 2 deletions packages/cli/package/src/lib/chain/commitment.ts
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ import {
type PeerAndOfferNameFlags,
CC_IDS_FLAG_NAME,
FINISH_COMMITMENT_FLAG_NAME,
FLT_SYMBOL,
} from "../const.js";
import { dbg } from "../dbg.js";
import {
Expand All @@ -40,6 +41,8 @@ import {
sign,
multicallRead,
type MulticallReadItem,
ensureJsonRpcProvider,
sendRawTransaction,
} from "../dealClient.js";
import { getCCDetails, getCCIdsByHexPeerIds } from "../gql/gql.js";
import type {
Expand All @@ -50,7 +53,7 @@ import { secondsToDate } from "../helpers/bigintOps.js";
import { stringifyUnknown } from "../helpers/stringifyUnknown.js";
import { bigintToStr, numToStr } from "../helpers/typesafeStringify.js";
import { splitErrorsAndResults, commaSepStrToArr } from "../helpers/utils.js";
import { input } from "../prompt.js";
import { confirm, input } from "../prompt.js";
import {
resolveComputePeersByNames,
type ResolvedComputePeer,
Expand All @@ -61,7 +64,7 @@ import {
peerIdBase58ToHexString,
peerIdHexStringToBase58String,
} from "./conversions.js";
import { fltFormatWithSymbol } from "./currencies.js";
import { fltFormatWithSymbol, fltParse } from "./currencies.js";

const HUNDRED_PERCENT = 100;

Expand Down Expand Up @@ -246,8 +249,83 @@ async function getCommitmentsIds(
return getComputePeersWithCCIds(await resolveComputePeersByNames({ flags }));
}

const MIN_PEER_TOKENS_FLT_STR = "10";

async function ensurePeersHaveEnoughTokens(
computePeers: ResolvedComputePeer[],
) {
const MIN_PEER_TOKENS = await fltParse(MIN_PEER_TOKENS_FLT_STR);
const jsonRpcProvider = await ensureJsonRpcProvider();

const peersWithoutEnoughTokens = (
await Promise.all(
computePeers.map(async (cp) => {
const balance = await jsonRpcProvider.getBalance(cp.walletAddress);
return { ...cp, balance };
}),
)
).filter(({ balance }) => {
return balance < MIN_PEER_TOKENS;
});

if (
peersWithoutEnoughTokens.length > 0 &&
(await confirm({
message: `The following peers don't have enough tokens (${await fltFormatWithSymbol(MIN_PEER_TOKENS)}) in their wallets to work properly:\n${(
await Promise.all(
peersWithoutEnoughTokens.map(async ({ name, balance }) => {
return `${name}: ${await fltFormatWithSymbol(balance)}`;
}),
)
).join("\n")}\nDo you want to ensure they do?`,
default: true,
}))
) {
const targetTokens = await fltParse(
await input({
message: `Enter the amount of ${FLT_SYMBOL} tokens (min: ${await fltFormatWithSymbol(
MIN_PEER_TOKENS,
)}) that you want to have on the wallets of peers: ${peersWithoutEnoughTokens
.map(({ name }) => {
return name;
})
.join(", ")}`,
default: MIN_PEER_TOKENS_FLT_STR,
async validate(val: string) {
let parsedVal: bigint;

try {
parsedVal = await fltParse(val);
} catch {
return "Amount must be a positive number";
}

return parsedVal > 0 || "Amount must be a positive number";
},
}),
);

for (const { balance, walletAddress, name } of peersWithoutEnoughTokens) {
const tokensToDistribute = targetTokens - balance;
const formattedAmount = await fltFormatWithSymbol(tokensToDistribute);

const txReceipt = await sendRawTransaction(
`Distribute ${formattedAmount} to ${name} (${walletAddress})`,
{ to: walletAddress, value: tokensToDistribute },
);

commandObj.logToStderr(
`Successfully distributed ${color.yellow(formattedAmount)} to ${color.yellow(
name,
)} with tx hash: ${color.yellow(txReceipt.hash)}`,
);
}
}
}

export async function createCommitments(flags: PeerAndOfferNameFlags) {
const computePeers = await resolveComputePeersByNames({ flags });
await ensurePeersHaveEnoughTokens(computePeers);
const { contracts } = await getContracts();
const precision = await contracts.diamond.precision();
const { ZeroAddress } = await import("ethers");
Expand Down
6 changes: 4 additions & 2 deletions packages/cli/package/src/lib/chain/currencies.ts
Original file line number Diff line number Diff line change
Expand Up @@ -54,9 +54,11 @@ async function getPtDecimals() {
if (ptDecimalsPromise === undefined) {
ptDecimalsPromise = (async () => {
const { id } = await import("ethers");
const { readonlyContracts, provider } = await getReadonlyContracts();

const decimalsRaw = await provider.call({
const { readonlyContracts, jsonRpcProvider } =
await getReadonlyContracts();

const decimalsRaw = await jsonRpcProvider.call({
to: readonlyContracts.deployment.usdc,
data: id("decimals()").substring(0, 10),
});
Expand Down
28 changes: 11 additions & 17 deletions packages/cli/package/src/lib/chain/distributeToNox.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,8 +15,6 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/

import assert from "assert";

import { color } from "@oclif/color";

import { commandObj } from "../commandObj.js";
Expand All @@ -29,6 +27,7 @@ import {
getWallet,
sendRawTransaction,
getSignerAddress,
ensureJsonRpcProvider,
} from "../dealClient.js";
import { input } from "../prompt.js";
import { resolveComputePeersByNames } from "../resolveComputePeersByNames.js";
Expand All @@ -47,19 +46,16 @@ export async function distributeToPeer(
}));

const parsedAmount = await fltParse(amount);
const formattedAmount = color.yellow(await fltFormatWithSymbol(parsedAmount));
const formattedAmount = await fltFormatWithSymbol(parsedAmount);

for (const computePeer of computePeers) {
const txReceipt = await sendRawTransaction(
`Distribute ${await fltFormatWithSymbol(parsedAmount)} to ${computePeer.name} (${computePeer.walletAddress})`,
{
to: computePeer.walletAddress,
value: parsedAmount,
},
`Distribute ${formattedAmount} to ${computePeer.name} (${computePeer.walletAddress})`,
{ to: computePeer.walletAddress, value: parsedAmount },
);

commandObj.logToStderr(
`Successfully distributed ${formattedAmount} to ${color.yellow(
`Successfully distributed ${color.yellow(formattedAmount)} to ${color.yellow(
computePeer.name,
)} with tx hash: ${color.yellow(txReceipt.hash)}`,
);
Expand Down Expand Up @@ -136,17 +132,17 @@ async function withdrawMaxAmount({
peerWalletKey,
peerName,
}: WithdrawMaxAmountArgs) {
const providerAddress = await getSignerAddress();
const peerWallet = await getWallet(peerWalletKey);
const peerAddress = await getSignerAddress();
const peerAddress = peerWallet.address;

const gasLimit = await peerWallet.estimateGas({
to: peerAddress,
to: providerAddress,
value: 0n,
});

const peerProvider = peerWallet.provider;
assert(peerProvider !== null, "Unreachable. We ensure provider is not null");
const gasPrice = await peerProvider.getFeeData();
const jsonRpcProvider = await ensureJsonRpcProvider();
const gasPrice = await jsonRpcProvider.getFeeData();

if (
gasPrice.maxFeePerGas === null ||
Expand All @@ -160,7 +156,7 @@ async function withdrawMaxAmount({
const feeAmount =
(gasPrice.maxFeePerGas + gasPrice.maxPriorityFeePerGas) * gasLimit;

const totalBalance = await peerProvider.getBalance(peerAddress);
const totalBalance = await jsonRpcProvider.getBalance(peerAddress);
const amountBigInt = totalBalance - feeAmount;

if (amountBigInt <= 0n) {
Expand All @@ -175,8 +171,6 @@ async function withdrawMaxAmount({
};
}

const providerAddress = await getSignerAddress();

const result = {
txReceipt: await sendRawTransaction(
`Withdraw max amount of ${await fltFormatWithSymbol(amountBigInt)} from ${peerName} (${peerAddress}) to ${providerAddress}`,
Expand Down
22 changes: 11 additions & 11 deletions packages/cli/package/src/lib/dealClient.ts
Original file line number Diff line number Diff line change
Expand Up @@ -53,19 +53,19 @@ import { setTryTimeout } from "./helpers/setTryTimeout.js";
import { stringifyUnknown } from "./helpers/stringifyUnknown.js";
import { createTransaction, getAddressFromConnector } from "./server.js";

let provider: Provider | undefined = undefined;
let jsonRpcProvider: Provider | undefined = undefined;
let readonlyContracts: Contracts | undefined = undefined;

export async function getReadonlyContracts() {
if (provider === undefined) {
provider = await ensureProvider();
if (jsonRpcProvider === undefined) {
jsonRpcProvider = await ensureJsonRpcProvider();
}

if (readonlyContracts === undefined) {
readonlyContracts = await createContracts(provider);
readonlyContracts = await createContracts(jsonRpcProvider);
}

return { readonlyContracts, provider };
return { readonlyContracts, jsonRpcProvider };
}

let providerOrWallet: Provider | Wallet | undefined = undefined;
Expand All @@ -82,7 +82,7 @@ export async function getContracts() {

if (providerOrWallet === undefined || contracts === undefined) {
providerOrWallet = await (privKey === undefined
? ensureProvider()
? ensureJsonRpcProvider()
: getWallet(privKey));

contracts = await createContracts(providerOrWallet);
Expand Down Expand Up @@ -159,22 +159,22 @@ async function createContracts(signerOrProvider: Provider | Signer) {
return contracts;
}

async function ensureProvider(): Promise<Provider> {
if (provider === undefined) {
export async function ensureJsonRpcProvider(): Promise<Provider> {
if (jsonRpcProvider === undefined) {
const { JsonRpcProvider } = await import("ethers");

provider = new JsonRpcProvider(await getRpcUrl(), {
jsonRpcProvider = new JsonRpcProvider(await getRpcUrl(), {
chainId: await getChainId(),
name: await getNetworkName(),
});
}

return provider;
return jsonRpcProvider;
}

export async function getWallet(privKey: string): Promise<Wallet> {
const { Wallet } = await import("ethers");
return new Wallet(privKey, await ensureProvider());
return new Wallet(privKey, await ensureJsonRpcProvider());
}

const DEFAULT_OVERRIDES: TransactionRequest = {
Expand Down
72 changes: 12 additions & 60 deletions packages/cli/package/test/tests/provider.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -128,19 +128,13 @@ describe("provider tests", () => {
await providerConfig.$commit();

const TEST_DEFAULT = {
flags: {
...PRIV_KEY_1,
[OFFER_FLAG_NAME]: NEW_OFFER_NAME,
},
flags: { ...PRIV_KEY_1, [OFFER_FLAG_NAME]: NEW_OFFER_NAME },
cwd,
} as const;

await fluence({
args: ["provider", "tokens-distribute"],
flags: {
...TEST_DEFAULT.flags,
amount: "10",
},
flags: { ...TEST_DEFAULT.flags, amount: "5" },
cwd,
});

Expand All @@ -151,60 +145,18 @@ describe("provider tests", () => {
await providerConfig.$commit();
await fluence({ args: ["provider", "update"], flags: PRIV_KEY_1, cwd });
await checkProviderNameIsCorrect(cwd, NEW_PROVIDER_NAME);

await fluence({
args: ["provider", "offer-create"],
...TEST_DEFAULT,
});

await fluence({
args: ["provider", "cc-create"],
...TEST_DEFAULT,
});

await fluence({
args: ["provider", "cc-info"],
...TEST_DEFAULT,
});

await fluence({
args: ["provider", "cc-activate"],
...TEST_DEFAULT,
});

await fluence({
args: ["provider", "cc-info"],
...TEST_DEFAULT,
});

await fluence({ args: ["provider", "offer-create"], ...TEST_DEFAULT });
await fluence({ args: ["provider", "cc-create"], ...TEST_DEFAULT });
await fluence({ args: ["provider", "cc-info"], ...TEST_DEFAULT });
await fluence({ args: ["provider", "cc-activate"], ...TEST_DEFAULT });
await fluence({ args: ["provider", "cc-info"], ...TEST_DEFAULT });
await sleepSeconds(5);

await fluence({
args: ["provider", "cc-info"],
...TEST_DEFAULT,
});

await fluence({ args: ["provider", "cc-info"], ...TEST_DEFAULT });
await sleepSeconds(CC_DURATION_SECONDS);

await fluence({
args: ["provider", "cc-info"],
...TEST_DEFAULT,
});

await fluence({
args: ["provider", "cc-finish"],
...TEST_DEFAULT,
});

await fluence({
args: ["provider", "cc-info"],
...TEST_DEFAULT,
});

await fluence({
args: ["provider", "offer-remove"],
...TEST_DEFAULT,
});
await fluence({ args: ["provider", "cc-info"], ...TEST_DEFAULT });
await fluence({ args: ["provider", "cc-finish"], ...TEST_DEFAULT });
await fluence({ args: ["provider", "cc-info"], ...TEST_DEFAULT });
await fluence({ args: ["provider", "offer-remove"], ...TEST_DEFAULT });
},
);

Expand Down

0 comments on commit dfb59a5

Please sign in to comment.