-
Notifications
You must be signed in to change notification settings - Fork 9
Feature/sync accounts lists #579
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
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change | ||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| @@ -1,5 +1,5 @@ | ||||||||||||||
| import { DONATION_CONTRACT_ACCOUNT_ID } from "@/common/_config"; | ||||||||||||||
| import { contractApi } from "@/common/blockchains/near-protocol/client"; | ||||||||||||||
| import { contractApi, walletApi } from "@/common/blockchains/near-protocol/client"; | ||||||||||||||
| import { FULL_TGAS } from "@/common/constants"; | ||||||||||||||
| import type { IndivisibleUnits } from "@/common/types"; | ||||||||||||||
|
|
||||||||||||||
|
|
@@ -10,6 +10,11 @@ import { | |||||||||||||
| DirectDonationConfig, | ||||||||||||||
| } from "./interfaces"; | ||||||||||||||
|
|
||||||||||||||
| export type DirectDonateResult = { | ||||||||||||||
| donation: DirectDonation; | ||||||||||||||
| txHash: string | null; | ||||||||||||||
| }; | ||||||||||||||
|
|
||||||||||||||
| const donationContractApi = contractApi({ | ||||||||||||||
| contractId: DONATION_CONTRACT_ACCOUNT_ID, | ||||||||||||||
| }); | ||||||||||||||
|
|
@@ -41,25 +46,145 @@ export const get_donations_for_donor = (args: { donor_id: string }) => | |||||||||||||
| args, | ||||||||||||||
| }); | ||||||||||||||
|
|
||||||||||||||
| export const donate = (args: DirectDonationArgs, depositAmountYocto: IndivisibleUnits) => | ||||||||||||||
| donationContractApi.call<typeof args, DirectDonation>("donate", { | ||||||||||||||
| export const donate = async ( | ||||||||||||||
| args: DirectDonationArgs, | ||||||||||||||
| depositAmountYocto: IndivisibleUnits, | ||||||||||||||
| ): Promise<DirectDonateResult> => { | ||||||||||||||
| const wallet = await walletApi.ensureWallet(); | ||||||||||||||
| const signerId = walletApi.accountId; | ||||||||||||||
|
|
||||||||||||||
| if (!signerId) { | ||||||||||||||
| throw new Error("Wallet is not signed in."); | ||||||||||||||
| } | ||||||||||||||
|
|
||||||||||||||
| const { actionCreators } = await import("@near-js/transactions"); | ||||||||||||||
| const { providers } = await import("near-api-js"); | ||||||||||||||
|
|
||||||||||||||
| const action = actionCreators.functionCall( | ||||||||||||||
| "donate", | ||||||||||||||
| args, | ||||||||||||||
| deposit: depositAmountYocto, | ||||||||||||||
| gas: FULL_TGAS, | ||||||||||||||
| callbackUrl: window.location.href, | ||||||||||||||
| }); | ||||||||||||||
| BigInt(FULL_TGAS), | ||||||||||||||
| BigInt(depositAmountYocto), | ||||||||||||||
| ); | ||||||||||||||
|
|
||||||||||||||
| let outcome: any; | ||||||||||||||
| const walletAny = wallet as any; | ||||||||||||||
|
|
||||||||||||||
| if ("signAndSendTransaction" in walletAny) { | ||||||||||||||
| outcome = await walletAny.signAndSendTransaction({ | ||||||||||||||
| signerId, | ||||||||||||||
| receiverId: DONATION_CONTRACT_ACCOUNT_ID, | ||||||||||||||
| actions: [action], | ||||||||||||||
| }); | ||||||||||||||
| } else if ("signAndSendTransactions" in walletAny) { | ||||||||||||||
| const results = await walletAny.signAndSendTransactions({ | ||||||||||||||
| transactions: [ | ||||||||||||||
| { | ||||||||||||||
| receiverId: DONATION_CONTRACT_ACCOUNT_ID, | ||||||||||||||
| actions: [action], | ||||||||||||||
| }, | ||||||||||||||
| ], | ||||||||||||||
| }); | ||||||||||||||
|
|
||||||||||||||
| outcome = Array.isArray(results) ? results[0] : results; | ||||||||||||||
| } else { | ||||||||||||||
| throw new Error("Wallet does not support transaction signing"); | ||||||||||||||
| } | ||||||||||||||
|
|
||||||||||||||
| const txHash = outcome?.transaction?.hash || outcome?.transaction_outcome?.id || null; | ||||||||||||||
| const donation = providers.getTransactionLastResult(outcome) as DirectDonation; | ||||||||||||||
|
|
||||||||||||||
| return { donation, txHash }; | ||||||||||||||
| }; | ||||||||||||||
|
|
||||||||||||||
| export type DirectBatchDonateResult = { | ||||||||||||||
| donations: DirectDonation[]; | ||||||||||||||
| txHash: string | null; | ||||||||||||||
| }; | ||||||||||||||
|
|
||||||||||||||
| export const donateBatch = (txInputs: DirectBatchDonationItem[]) => | ||||||||||||||
| donationContractApi.callMultiple<DirectDonationArgs>( | ||||||||||||||
| txInputs.map(({ amountYoctoNear, ...txInput }) => ({ | ||||||||||||||
| method: "donate", | ||||||||||||||
| deposit: amountYoctoNear, | ||||||||||||||
| gas: FULL_TGAS, | ||||||||||||||
| export const donateBatch = async ( | ||||||||||||||
| txInputs: DirectBatchDonationItem[], | ||||||||||||||
| ): Promise<DirectBatchDonateResult> => { | ||||||||||||||
| const wallet = await walletApi.ensureWallet(); | ||||||||||||||
| const signerId = walletApi.accountId; | ||||||||||||||
|
|
||||||||||||||
| ...txInput, | ||||||||||||||
| })), | ||||||||||||||
| if (!signerId) { | ||||||||||||||
| throw new Error("Wallet is not signed in."); | ||||||||||||||
| } | ||||||||||||||
|
|
||||||||||||||
| const { actionCreators } = await import("@near-js/transactions"); | ||||||||||||||
| const { providers } = await import("near-api-js"); | ||||||||||||||
|
|
||||||||||||||
| // Create actions for each donation | ||||||||||||||
| const actions = txInputs.map(({ amountYoctoNear, args }) => | ||||||||||||||
| actionCreators.functionCall("donate", args, BigInt(FULL_TGAS), BigInt(amountYoctoNear)), | ||||||||||||||
| ); | ||||||||||||||
|
Comment on lines
+119
to
121
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🧩 Analysis chain🏁 Script executed: rg -n "FULL_TGAS\s*=" --type=ts -B2 -A2 | head -50Repository: PotLock/potlock-nextjs-app Length of output: 318 🏁 Script executed: # Also check where FULL_TGAS is imported from
rg -n "FULL_TGAS" src/common/contracts/core/donation/client.ts -B5 -A5 | head -30Repository: PotLock/potlock-nextjs-app Length of output: 1049 🏁 Script executed: # Check the entire donateBatch function context around lines 119-121
cat -n src/common/contracts/core/donation/client.ts | sed -n '105,140p'Repository: PotLock/potlock-nextjs-app Length of output: 1505 🏁 Script executed: # Find calls to donateBatch
rg -n "donateBatch" --type=ts -B2 -A2Repository: PotLock/potlock-nextjs-app Length of output: 1703 🏁 Script executed: # Check if the signAndSendTransactions fallback path (line 133-140) has the same gas issue
cat -n src/common/contracts/core/donation/client.ts | sed -n '133,150p'Repository: PotLock/potlock-nextjs-app Length of output: 740 🏁 Script executed: # Check if there's any validation on txInputs.length before calling donateBatch
rg -n "DirectBatchDonationItem" --type=ts -B3 -A3 | head -60Repository: PotLock/potlock-nextjs-app Length of output: 4047 Potential transaction gas overflow in batch donations. Each action is allocated Divide the gas budget across actions: Proposed fix const actions = txInputs.map(({ amountYoctoNear, args }) =>
actionCreators.functionCall("donate", args, BigInt(FULL_TGAS) / BigInt(txInputs.length), BigInt(amountYoctoNear)),
);📝 Committable suggestion
Suggested change
🤖 Prompt for AI Agents |
||||||||||||||
|
|
||||||||||||||
| let outcome: any; | ||||||||||||||
| const walletAny = wallet as any; | ||||||||||||||
|
|
||||||||||||||
| if ("signAndSendTransaction" in walletAny) { | ||||||||||||||
| // Single transaction with multiple actions | ||||||||||||||
| outcome = await walletAny.signAndSendTransaction({ | ||||||||||||||
| signerId, | ||||||||||||||
| receiverId: DONATION_CONTRACT_ACCOUNT_ID, | ||||||||||||||
| actions, | ||||||||||||||
| }); | ||||||||||||||
| } else if ("signAndSendTransactions" in walletAny) { | ||||||||||||||
| // For wallets that only support signAndSendTransactions | ||||||||||||||
| const results = await walletAny.signAndSendTransactions({ | ||||||||||||||
| transactions: [ | ||||||||||||||
| { | ||||||||||||||
| receiverId: DONATION_CONTRACT_ACCOUNT_ID, | ||||||||||||||
| actions, | ||||||||||||||
| }, | ||||||||||||||
| ], | ||||||||||||||
| }); | ||||||||||||||
|
|
||||||||||||||
| outcome = Array.isArray(results) ? results[0] : results; | ||||||||||||||
| } else { | ||||||||||||||
| throw new Error("Wallet does not support transaction signing"); | ||||||||||||||
| } | ||||||||||||||
|
|
||||||||||||||
| const txHash = outcome?.transaction?.hash || outcome?.transaction_outcome?.id || null; | ||||||||||||||
|
|
||||||||||||||
| // Parse all donations from the outcome | ||||||||||||||
| const donations: DirectDonation[] = []; | ||||||||||||||
|
|
||||||||||||||
| if (outcome?.receipts_outcome) { | ||||||||||||||
| for (const receipt of outcome.receipts_outcome) { | ||||||||||||||
| const successValue = receipt?.outcome?.status?.SuccessValue; | ||||||||||||||
|
|
||||||||||||||
| if (successValue) { | ||||||||||||||
| try { | ||||||||||||||
| const parsed = JSON.parse(atob(successValue)); | ||||||||||||||
|
|
||||||||||||||
| if (parsed && "recipient_id" in parsed && "donor_id" in parsed) { | ||||||||||||||
| donations.push(parsed as DirectDonation); | ||||||||||||||
| } | ||||||||||||||
| } catch { | ||||||||||||||
| // Not valid JSON, skip | ||||||||||||||
| } | ||||||||||||||
| } | ||||||||||||||
| } | ||||||||||||||
| } | ||||||||||||||
|
|
||||||||||||||
| // Fallback: try to get last result | ||||||||||||||
| if (donations.length === 0) { | ||||||||||||||
| try { | ||||||||||||||
| const lastResult = providers.getTransactionLastResult(outcome); | ||||||||||||||
|
|
||||||||||||||
| if (lastResult && typeof lastResult === "object" && "recipient_id" in lastResult) { | ||||||||||||||
| donations.push(lastResult as DirectDonation); | ||||||||||||||
| } | ||||||||||||||
| } catch { | ||||||||||||||
| // Ignore | ||||||||||||||
| } | ||||||||||||||
| } | ||||||||||||||
|
|
||||||||||||||
| return { donations, txHash }; | ||||||||||||||
| }; | ||||||||||||||
|
|
||||||||||||||
| export const storage_deposit = (depositAmountYocto: IndivisibleUnits) => | ||||||||||||||
| donationContractApi.call<{}, IndivisibleUnits>("storage_deposit", { | ||||||||||||||
| deposit: depositAmountYocto, | ||||||||||||||
|
|
||||||||||||||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Unsafe cast of transaction result to
DirectDonation.providers.getTransactionLastResult(outcome)can returnnull,undefined, or an unexpected shape. Theas DirectDonationcast silently accepts anything, so callers may receivenullasdonationand later fail. Consider adding a validation check (e.g., duck-typing likedonateBatchdoes at line 162) or at minimum handling a null result.🤖 Prompt for AI Agents