Skip to content

Commit dfb59a5

Browse files
authored
feat: suggest to distribute tokens on cc-create if not enough (#1097)
* feat: suggest to distribute tokens on cc-create if not enough * use min 10 FLT
1 parent ad7b5e7 commit dfb59a5

File tree

5 files changed

+118
-92
lines changed

5 files changed

+118
-92
lines changed

packages/cli/package/src/lib/chain/commitment.ts

Lines changed: 80 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@ import {
3030
type PeerAndOfferNameFlags,
3131
CC_IDS_FLAG_NAME,
3232
FINISH_COMMITMENT_FLAG_NAME,
33+
FLT_SYMBOL,
3334
} from "../const.js";
3435
import { dbg } from "../dbg.js";
3536
import {
@@ -40,6 +41,8 @@ import {
4041
sign,
4142
multicallRead,
4243
type MulticallReadItem,
44+
ensureJsonRpcProvider,
45+
sendRawTransaction,
4346
} from "../dealClient.js";
4447
import { getCCDetails, getCCIdsByHexPeerIds } from "../gql/gql.js";
4548
import type {
@@ -50,7 +53,7 @@ import { secondsToDate } from "../helpers/bigintOps.js";
5053
import { stringifyUnknown } from "../helpers/stringifyUnknown.js";
5154
import { bigintToStr, numToStr } from "../helpers/typesafeStringify.js";
5255
import { splitErrorsAndResults, commaSepStrToArr } from "../helpers/utils.js";
53-
import { input } from "../prompt.js";
56+
import { confirm, input } from "../prompt.js";
5457
import {
5558
resolveComputePeersByNames,
5659
type ResolvedComputePeer,
@@ -61,7 +64,7 @@ import {
6164
peerIdBase58ToHexString,
6265
peerIdHexStringToBase58String,
6366
} from "./conversions.js";
64-
import { fltFormatWithSymbol } from "./currencies.js";
67+
import { fltFormatWithSymbol, fltParse } from "./currencies.js";
6568

6669
const HUNDRED_PERCENT = 100;
6770

@@ -246,8 +249,83 @@ async function getCommitmentsIds(
246249
return getComputePeersWithCCIds(await resolveComputePeersByNames({ flags }));
247250
}
248251

252+
const MIN_PEER_TOKENS_FLT_STR = "10";
253+
254+
async function ensurePeersHaveEnoughTokens(
255+
computePeers: ResolvedComputePeer[],
256+
) {
257+
const MIN_PEER_TOKENS = await fltParse(MIN_PEER_TOKENS_FLT_STR);
258+
const jsonRpcProvider = await ensureJsonRpcProvider();
259+
260+
const peersWithoutEnoughTokens = (
261+
await Promise.all(
262+
computePeers.map(async (cp) => {
263+
const balance = await jsonRpcProvider.getBalance(cp.walletAddress);
264+
return { ...cp, balance };
265+
}),
266+
)
267+
).filter(({ balance }) => {
268+
return balance < MIN_PEER_TOKENS;
269+
});
270+
271+
if (
272+
peersWithoutEnoughTokens.length > 0 &&
273+
(await confirm({
274+
message: `The following peers don't have enough tokens (${await fltFormatWithSymbol(MIN_PEER_TOKENS)}) in their wallets to work properly:\n${(
275+
await Promise.all(
276+
peersWithoutEnoughTokens.map(async ({ name, balance }) => {
277+
return `${name}: ${await fltFormatWithSymbol(balance)}`;
278+
}),
279+
)
280+
).join("\n")}\nDo you want to ensure they do?`,
281+
default: true,
282+
}))
283+
) {
284+
const targetTokens = await fltParse(
285+
await input({
286+
message: `Enter the amount of ${FLT_SYMBOL} tokens (min: ${await fltFormatWithSymbol(
287+
MIN_PEER_TOKENS,
288+
)}) that you want to have on the wallets of peers: ${peersWithoutEnoughTokens
289+
.map(({ name }) => {
290+
return name;
291+
})
292+
.join(", ")}`,
293+
default: MIN_PEER_TOKENS_FLT_STR,
294+
async validate(val: string) {
295+
let parsedVal: bigint;
296+
297+
try {
298+
parsedVal = await fltParse(val);
299+
} catch {
300+
return "Amount must be a positive number";
301+
}
302+
303+
return parsedVal > 0 || "Amount must be a positive number";
304+
},
305+
}),
306+
);
307+
308+
for (const { balance, walletAddress, name } of peersWithoutEnoughTokens) {
309+
const tokensToDistribute = targetTokens - balance;
310+
const formattedAmount = await fltFormatWithSymbol(tokensToDistribute);
311+
312+
const txReceipt = await sendRawTransaction(
313+
`Distribute ${formattedAmount} to ${name} (${walletAddress})`,
314+
{ to: walletAddress, value: tokensToDistribute },
315+
);
316+
317+
commandObj.logToStderr(
318+
`Successfully distributed ${color.yellow(formattedAmount)} to ${color.yellow(
319+
name,
320+
)} with tx hash: ${color.yellow(txReceipt.hash)}`,
321+
);
322+
}
323+
}
324+
}
325+
249326
export async function createCommitments(flags: PeerAndOfferNameFlags) {
250327
const computePeers = await resolveComputePeersByNames({ flags });
328+
await ensurePeersHaveEnoughTokens(computePeers);
251329
const { contracts } = await getContracts();
252330
const precision = await contracts.diamond.precision();
253331
const { ZeroAddress } = await import("ethers");

packages/cli/package/src/lib/chain/currencies.ts

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -54,9 +54,11 @@ async function getPtDecimals() {
5454
if (ptDecimalsPromise === undefined) {
5555
ptDecimalsPromise = (async () => {
5656
const { id } = await import("ethers");
57-
const { readonlyContracts, provider } = await getReadonlyContracts();
5857

59-
const decimalsRaw = await provider.call({
58+
const { readonlyContracts, jsonRpcProvider } =
59+
await getReadonlyContracts();
60+
61+
const decimalsRaw = await jsonRpcProvider.call({
6062
to: readonlyContracts.deployment.usdc,
6163
data: id("decimals()").substring(0, 10),
6264
});

packages/cli/package/src/lib/chain/distributeToNox.ts

Lines changed: 11 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -15,8 +15,6 @@
1515
* along with this program. If not, see <http://www.gnu.org/licenses/>.
1616
*/
1717

18-
import assert from "assert";
19-
2018
import { color } from "@oclif/color";
2119

2220
import { commandObj } from "../commandObj.js";
@@ -29,6 +27,7 @@ import {
2927
getWallet,
3028
sendRawTransaction,
3129
getSignerAddress,
30+
ensureJsonRpcProvider,
3231
} from "../dealClient.js";
3332
import { input } from "../prompt.js";
3433
import { resolveComputePeersByNames } from "../resolveComputePeersByNames.js";
@@ -47,19 +46,16 @@ export async function distributeToPeer(
4746
}));
4847

4948
const parsedAmount = await fltParse(amount);
50-
const formattedAmount = color.yellow(await fltFormatWithSymbol(parsedAmount));
49+
const formattedAmount = await fltFormatWithSymbol(parsedAmount);
5150

5251
for (const computePeer of computePeers) {
5352
const txReceipt = await sendRawTransaction(
54-
`Distribute ${await fltFormatWithSymbol(parsedAmount)} to ${computePeer.name} (${computePeer.walletAddress})`,
55-
{
56-
to: computePeer.walletAddress,
57-
value: parsedAmount,
58-
},
53+
`Distribute ${formattedAmount} to ${computePeer.name} (${computePeer.walletAddress})`,
54+
{ to: computePeer.walletAddress, value: parsedAmount },
5955
);
6056

6157
commandObj.logToStderr(
62-
`Successfully distributed ${formattedAmount} to ${color.yellow(
58+
`Successfully distributed ${color.yellow(formattedAmount)} to ${color.yellow(
6359
computePeer.name,
6460
)} with tx hash: ${color.yellow(txReceipt.hash)}`,
6561
);
@@ -136,17 +132,17 @@ async function withdrawMaxAmount({
136132
peerWalletKey,
137133
peerName,
138134
}: WithdrawMaxAmountArgs) {
135+
const providerAddress = await getSignerAddress();
139136
const peerWallet = await getWallet(peerWalletKey);
140-
const peerAddress = await getSignerAddress();
137+
const peerAddress = peerWallet.address;
141138

142139
const gasLimit = await peerWallet.estimateGas({
143-
to: peerAddress,
140+
to: providerAddress,
144141
value: 0n,
145142
});
146143

147-
const peerProvider = peerWallet.provider;
148-
assert(peerProvider !== null, "Unreachable. We ensure provider is not null");
149-
const gasPrice = await peerProvider.getFeeData();
144+
const jsonRpcProvider = await ensureJsonRpcProvider();
145+
const gasPrice = await jsonRpcProvider.getFeeData();
150146

151147
if (
152148
gasPrice.maxFeePerGas === null ||
@@ -160,7 +156,7 @@ async function withdrawMaxAmount({
160156
const feeAmount =
161157
(gasPrice.maxFeePerGas + gasPrice.maxPriorityFeePerGas) * gasLimit;
162158

163-
const totalBalance = await peerProvider.getBalance(peerAddress);
159+
const totalBalance = await jsonRpcProvider.getBalance(peerAddress);
164160
const amountBigInt = totalBalance - feeAmount;
165161

166162
if (amountBigInt <= 0n) {
@@ -175,8 +171,6 @@ async function withdrawMaxAmount({
175171
};
176172
}
177173

178-
const providerAddress = await getSignerAddress();
179-
180174
const result = {
181175
txReceipt: await sendRawTransaction(
182176
`Withdraw max amount of ${await fltFormatWithSymbol(amountBigInt)} from ${peerName} (${peerAddress}) to ${providerAddress}`,

packages/cli/package/src/lib/dealClient.ts

Lines changed: 11 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -53,19 +53,19 @@ import { setTryTimeout } from "./helpers/setTryTimeout.js";
5353
import { stringifyUnknown } from "./helpers/stringifyUnknown.js";
5454
import { createTransaction, getAddressFromConnector } from "./server.js";
5555

56-
let provider: Provider | undefined = undefined;
56+
let jsonRpcProvider: Provider | undefined = undefined;
5757
let readonlyContracts: Contracts | undefined = undefined;
5858

5959
export async function getReadonlyContracts() {
60-
if (provider === undefined) {
61-
provider = await ensureProvider();
60+
if (jsonRpcProvider === undefined) {
61+
jsonRpcProvider = await ensureJsonRpcProvider();
6262
}
6363

6464
if (readonlyContracts === undefined) {
65-
readonlyContracts = await createContracts(provider);
65+
readonlyContracts = await createContracts(jsonRpcProvider);
6666
}
6767

68-
return { readonlyContracts, provider };
68+
return { readonlyContracts, jsonRpcProvider };
6969
}
7070

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

8383
if (providerOrWallet === undefined || contracts === undefined) {
8484
providerOrWallet = await (privKey === undefined
85-
? ensureProvider()
85+
? ensureJsonRpcProvider()
8686
: getWallet(privKey));
8787

8888
contracts = await createContracts(providerOrWallet);
@@ -159,22 +159,22 @@ async function createContracts(signerOrProvider: Provider | Signer) {
159159
return contracts;
160160
}
161161

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

166-
provider = new JsonRpcProvider(await getRpcUrl(), {
166+
jsonRpcProvider = new JsonRpcProvider(await getRpcUrl(), {
167167
chainId: await getChainId(),
168168
name: await getNetworkName(),
169169
});
170170
}
171171

172-
return provider;
172+
return jsonRpcProvider;
173173
}
174174

175175
export async function getWallet(privKey: string): Promise<Wallet> {
176176
const { Wallet } = await import("ethers");
177-
return new Wallet(privKey, await ensureProvider());
177+
return new Wallet(privKey, await ensureJsonRpcProvider());
178178
}
179179

180180
const DEFAULT_OVERRIDES: TransactionRequest = {

packages/cli/package/test/tests/provider.test.ts

Lines changed: 12 additions & 60 deletions
Original file line numberDiff line numberDiff line change
@@ -128,19 +128,13 @@ describe("provider tests", () => {
128128
await providerConfig.$commit();
129129

130130
const TEST_DEFAULT = {
131-
flags: {
132-
...PRIV_KEY_1,
133-
[OFFER_FLAG_NAME]: NEW_OFFER_NAME,
134-
},
131+
flags: { ...PRIV_KEY_1, [OFFER_FLAG_NAME]: NEW_OFFER_NAME },
135132
cwd,
136133
} as const;
137134

138135
await fluence({
139136
args: ["provider", "tokens-distribute"],
140-
flags: {
141-
...TEST_DEFAULT.flags,
142-
amount: "10",
143-
},
137+
flags: { ...TEST_DEFAULT.flags, amount: "5" },
144138
cwd,
145139
});
146140

@@ -151,60 +145,18 @@ describe("provider tests", () => {
151145
await providerConfig.$commit();
152146
await fluence({ args: ["provider", "update"], flags: PRIV_KEY_1, cwd });
153147
await checkProviderNameIsCorrect(cwd, NEW_PROVIDER_NAME);
154-
155-
await fluence({
156-
args: ["provider", "offer-create"],
157-
...TEST_DEFAULT,
158-
});
159-
160-
await fluence({
161-
args: ["provider", "cc-create"],
162-
...TEST_DEFAULT,
163-
});
164-
165-
await fluence({
166-
args: ["provider", "cc-info"],
167-
...TEST_DEFAULT,
168-
});
169-
170-
await fluence({
171-
args: ["provider", "cc-activate"],
172-
...TEST_DEFAULT,
173-
});
174-
175-
await fluence({
176-
args: ["provider", "cc-info"],
177-
...TEST_DEFAULT,
178-
});
179-
148+
await fluence({ args: ["provider", "offer-create"], ...TEST_DEFAULT });
149+
await fluence({ args: ["provider", "cc-create"], ...TEST_DEFAULT });
150+
await fluence({ args: ["provider", "cc-info"], ...TEST_DEFAULT });
151+
await fluence({ args: ["provider", "cc-activate"], ...TEST_DEFAULT });
152+
await fluence({ args: ["provider", "cc-info"], ...TEST_DEFAULT });
180153
await sleepSeconds(5);
181-
182-
await fluence({
183-
args: ["provider", "cc-info"],
184-
...TEST_DEFAULT,
185-
});
186-
154+
await fluence({ args: ["provider", "cc-info"], ...TEST_DEFAULT });
187155
await sleepSeconds(CC_DURATION_SECONDS);
188-
189-
await fluence({
190-
args: ["provider", "cc-info"],
191-
...TEST_DEFAULT,
192-
});
193-
194-
await fluence({
195-
args: ["provider", "cc-finish"],
196-
...TEST_DEFAULT,
197-
});
198-
199-
await fluence({
200-
args: ["provider", "cc-info"],
201-
...TEST_DEFAULT,
202-
});
203-
204-
await fluence({
205-
args: ["provider", "offer-remove"],
206-
...TEST_DEFAULT,
207-
});
156+
await fluence({ args: ["provider", "cc-info"], ...TEST_DEFAULT });
157+
await fluence({ args: ["provider", "cc-finish"], ...TEST_DEFAULT });
158+
await fluence({ args: ["provider", "cc-info"], ...TEST_DEFAULT });
159+
await fluence({ args: ["provider", "offer-remove"], ...TEST_DEFAULT });
208160
},
209161
);
210162

0 commit comments

Comments
 (0)