Skip to content
Open
Show file tree
Hide file tree
Changes from 3 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
20 changes: 19 additions & 1 deletion src/common/contracts/core/pot/client.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
import { calculateDepositByDataSize } from "@wpdas/naxios";
import { Big } from "big.js";
import { parseNearAmount } from "near-api-js/lib/utils/format";

import { type ByPotId, PotId } from "@/common/api/indexer";
import { nearProtocolClient } from "@/common/blockchains/near-protocol";
import { FULL_TGAS, ONE_HUNDREDTH_NEAR } from "@/common/constants";
import { FULL_TGAS, ONE_HUNDREDTH_NEAR, ONE_TGAS } from "@/common/constants";
import type { AccountId } from "@/common/types";

import {
Expand All @@ -18,6 +19,7 @@ import {
PotDonationArgs,
UpdatePotArgs,
} from "./interfaces";
import { calculateCallDeposit } from "./utils";

export const contractApi = (potId: string) =>
nearProtocolClient.naxiosInstance.contractApi({
Expand Down Expand Up @@ -107,6 +109,22 @@ export const get_payouts = async (args: { potId: string }) =>

// WRITE METHODS

export type ApplyArgs = {
message?: string;
};

export const apply = ({
potId,
args,
callbackUrl = window.location.href,
}: ByPotId & { args: ApplyArgs; callbackUrl?: string }) =>
contractApi(potId).call<typeof args, Application>("apply", {
args,
deposit: calculateCallDeposit({ functionName: "apply", args }),
gas: ONE_TGAS.mul(100).toString(),
callbackUrl,
});

export type ChallengePayoutsArgs = {
reason: string;
};
Expand Down
3 changes: 2 additions & 1 deletion src/common/contracts/core/pot/index.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
import * as potContractClient from "./client";
import * as potContractHooks from "./hooks";
import * as potContractUtils from "./utils";

export type * from "./hooks";
export * from "./interfaces";

export { potContractClient, potContractHooks };
export { potContractClient, potContractHooks, potContractUtils };
30 changes: 30 additions & 0 deletions src/common/contracts/core/pot/utils.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
import { calculateDepositByDataSize } from "@wpdas/naxios";
import { Big } from "big.js";

import { ONE_HUNDREDTH_NEAR } from "@/common/constants";
import { parseNearAmount } from "@/common/lib";
import type { IndivisibleUnits } from "@/common/types";

import type { ApplyArgs } from "./client";

type CallDepositParams =
| { functionName: "apply"; args: ApplyArgs }
| { functionName: string; args: {} };

export const calculateCallDeposit = ({
functionName,
args,
}: CallDepositParams): IndivisibleUnits => {
switch (functionName) {
case "apply": {
return (
parseNearAmount(Big(0.01).times(calculateDepositByDataSize(args)).toString()) ??
ONE_HUNDREDTH_NEAR
);
}

default: {
return "0";
}
}
};
26 changes: 24 additions & 2 deletions src/common/contracts/sputnikdao2/client.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
import { nearProtocolClient } from "@/common/blockchains/near-protocol";
import type { ByAccountId } from "@/common/types";
import { FULL_TGAS } from "@/common/constants";
import type { ByAccountId, IndivisibleUnits } from "@/common/types";

import type { Policy, ProposalOutput } from "./types";
import type { Policy, ProposalId, ProposalInput, ProposalOutput } from "./types";

export const get_policy = ({ accountId }: ByAccountId) =>
nearProtocolClient.naxiosInstance
Expand All @@ -20,3 +21,24 @@ export const get_proposals = ({ accountId, args }: ByAccountId & { args: GetProp
nearProtocolClient.naxiosInstance
.contractApi({ contractId: accountId })
.view<typeof args, ProposalOutput[]>("get_proposals", { args });

export type AddProposalArgs = {
proposal: ProposalInput;
};

export const add_proposal = ({
accountId,
proposalBond,
callbackUrl = window.location.href,
args,
}: ByAccountId & { proposalBond: IndivisibleUnits; callbackUrl?: string } & {
args: AddProposalArgs;
}) =>
nearProtocolClient.naxiosInstance
.contractApi({ contractId: accountId })
.call<typeof args, ProposalId>("add_proposal", {
args,
deposit: proposalBond,
gas: FULL_TGAS,
callbackUrl,
});
7 changes: 6 additions & 1 deletion src/common/contracts/sputnikdao2/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -87,7 +87,7 @@ export type ActionCall = {
args: string;

deposit: IndivisibleUnits;
gas: number;
gas: IndivisibleUnits;
};

export enum ProposalStatus {
Expand Down Expand Up @@ -149,6 +149,11 @@ export enum Vote {
Remove = 2,
}

export type ProposalInput = {
description: string;
kind: ProposalKind;
};

export type Proposal = {
proposer: AccountId;
description: string;
Expand Down
5 changes: 5 additions & 0 deletions src/common/lib/object.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
import { mapValues } from "remeda";

import { utf8StringToBase64 } from "./string";

type DeepPartial<T> = T extends object
? {
[P in keyof T]?: T[P] extends object ? DeepPartial<T[P]> : T[P];
Expand Down Expand Up @@ -52,3 +54,6 @@ export const nullifyEmptyStrings = mapValues((value: string | unknown) => {
return null;
} else return value;
});

export const objectToBase64Json = (obj: object | Record<string, unknown>): string =>
utf8StringToBase64(JSON.stringify(obj));
3 changes: 3 additions & 0 deletions src/common/lib/string.ts
Original file line number Diff line number Diff line change
Expand Up @@ -35,3 +35,6 @@ export const isValidHttpUrl = (value: string) => {
return false;
}
};

export const utf8StringToBase64 = (value: string): string =>
Buffer.from(value, "utf8").toString("base64");
2 changes: 1 addition & 1 deletion src/features/donation/components/user-entrypoints.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -72,7 +72,7 @@ export type DonateToListProjectsProps = ByListId & {};
export const DonateToListProjects: React.FC<DonateToListProjectsProps> = ({ listId }) => {
const { openDonationModal } = useDonationUserFlow({ listId });

return <Button onClick={openDonationModal}>{"Donate to list"}</Button>;
return <Button onClick={openDonationModal}>{"Donate to Projects"}</Button>;
};

export type DonationToCampaignProps = ByCampaignId &
Expand Down
3 changes: 2 additions & 1 deletion src/features/matching-pool-contribution/hooks/forms.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import { Pot } from "@/common/api/indexer";
import { naxiosInstance } from "@/common/blockchains/near-protocol/client";
import { FIFTY_TGAS, FULL_TGAS, MIN_PROPOSAL_DEPOSIT_FALLBACK, ONE_TGAS } from "@/common/constants";
import { sputnikDaoClient } from "@/common/contracts/sputnikdao2";
import { objectToBase64Json } from "@/common/lib";
import { useWalletUserSession } from "@/common/wallet";

import { MatchingPoolContributionInputs, matchingPoolFundingSchema } from "../model/schemas";
Expand Down Expand Up @@ -37,7 +38,7 @@ export const useMatchingPoolContributionForm = ({ potDetail }: { potDetail: Pot
method_name: "donate",
gas: FIFTY_TGAS,
deposit: parseNearAmount(formData.amountNEAR.toString()) || "0",
args: Buffer.from(JSON.stringify(args), "utf-8").toString("base64"),
args: objectToBase64Json(args),
};

const daoTransactionArgs = {
Expand Down
56 changes: 25 additions & 31 deletions src/features/pot-application/components/PotApplicationModal.tsx
Original file line number Diff line number Diff line change
@@ -1,91 +1,85 @@
// TODO!: Import from the UI kit instead and adjust the layout accordingly
import { Form } from "react-hook-form";

import { Pot } from "@/common/api/indexer";
import type { AccountId } from "@/common/types";
import { TextAreaField } from "@/common/ui/form/components";
import {
Button,
Dialog,
DialogContent,
DialogHeader,
DialogTitle,
Form,
FormField,
Spinner,
Textarea,
} from "@/common/ui/layout/components";
import { useWalletUserSession } from "@/common/wallet";

import { usePotApplicationForm } from "../hooks/forms";
import { type PotApplicationFormParams, usePotApplicationForm } from "../hooks/forms";

export type PotApplicationModalProps = {
export type PotApplicationModalProps = Pick<
PotApplicationFormParams,
"applicantAccountId" | "potConfig" | "onSuccess" | "onFailure"
> & {
open?: boolean;
onCloseClick?: () => void;
applicantAccountId: AccountId;
daoMode?: boolean;
potDetail: Pot;
};

export const PotApplicationModal: React.FC<PotApplicationModalProps> = ({
open,
onCloseClick,
applicantAccountId,
daoMode = false,
potDetail,
potConfig,
onSuccess,
onFailure,
}) => {
const walletUser = useWalletUserSession();

// Form settings
const { form, errors, onSubmit, inProgress } = usePotApplicationForm({
accountId: applicantAccountId,
const { form, onSubmit } = usePotApplicationForm({
applicantAccountId,
asDao: daoMode,
potDetail,
potConfig,
onSuccess,
onFailure,
});

return (
<Dialog open={open}>
<DialogContent className="max-w-130" onCloseClick={onCloseClick}>
<DialogHeader>
<DialogTitle>Apply to Pot</DialogTitle>
<DialogTitle>{`Apply to ${potConfig.name}`}</DialogTitle>
</DialogHeader>

<Form {...form} onSubmit={onSubmit}>
<div className="flex flex-col p-6">
{/*NEAR Input */}
<p className="my-2 break-words text-[16px] font-normal leading-[20px] text-neutral-700">
Application message <span style={{ color: "#DD3345" }}>*</span>
</p>

{/* Optional Message */}
<Form {...form}>
<form className="flex flex-col p-6" onSubmit={onSubmit}>
<FormField
control={form.control}
name="message"
render={({ field }) => (
<Textarea
<TextAreaField
label="Application Message"
placeholder="Your application message here..."
rows={5}
className="mt-2"
{...field}
error={errors.message?.message}
/>
)}
/>

<Button
disabled={!form.formState.isValid || inProgress}
disabled={!form.formState.isValid || form.formState.isSubmitting}
className="mt-6 min-w-[200px] self-end"
type="submit"
>
{inProgress ? (
{form.formState.isSubmitting ? (
<Spinner />
) : (
<>
{walletUser.isDaoRepresentative
? "Propose to Send Application"
: "Send application"}
? "Submit Application Proposal"
: "Send Application"}
</>
)}
</Button>
</div>
</form>
</Form>
</DialogContent>
</Dialog>
Expand Down
Loading
Loading