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

feat: suggest to distribute tokens on cc-create if not enough #1097

Merged
merged 2 commits into from
Jan 21, 2025
Merged
Show file tree
Hide file tree
Changes from all 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
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";
shamsartem marked this conversation as resolved.
Show resolved Hide resolved
},
}),
);

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
Loading