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

Check against allowance on create stream #88

Closed
wants to merge 13 commits into from
6 changes: 6 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,12 @@ In order to work on the Demo app, you need to compile the JS SDK (like above) as
$ npm run build --workspace=ui/lib
```

If you already had a build, you may need to run this command to be able to build the workspaces:

```sh
$ npm run clean
```

Then you are ready to contribute!

To run the demo app in development mode, do the following:
Expand Down
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@
"scripts": {
"format": "prettier -w ui && prettier -w sdk",
"lint": "eslint . --ext ts,tsx --report-unused-disable-directives --max-warnings 0",
"clean": "rm -rf js-dist",
"clean": "find . -name 'dist' -type d -exec rm -rf {} +",
"build": "bash build-script.sh"
},
"engines": {
Expand Down
7 changes: 4 additions & 3 deletions ui/app/src/CreatePage.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -72,12 +72,13 @@ const StreamPage = () => {
<div className="create-stream-component">
<CreateStream
streamManagerAddress={sm as `0x${string}`}
amountPerSecond={BigInt(100)}
tokenList={tokenList}
amountPerSecond={BigInt(10000000000000)}
cart={<Cart />}
productName={"simulation"}
registerStream={addStreams}
renderReasonCode={renderReasonCode}
handleTransactionStatus={handleTransactionStatus}
tokenList={tokenList}
cart={<Cart />}
/>
{/* CreateStream callback */}
<div className="tx-status-display">
Expand Down
14 changes: 7 additions & 7 deletions ui/app/src/StreamPage.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,9 @@ import { ConnectButton } from "@rainbow-me/rainbowkit";
import "./styles.css";
import StreamManager, { Stream } from "@apeworx/apepay";
import {
CancelStream,
UpdateStream,
StreamStatus,
CancelStream,
} from "@apeworx/apepay-react";
import {
usePublicClient,
Expand Down Expand Up @@ -80,13 +80,13 @@ const StreamPage = () => {
}, [SM, address, walletClient]);

const [streamInfo, setStreamInfo] = useState({
token: null as string | null,
amountPerSecond: null as bigint | null,
maxStreamLife: null as bigint | null,
fundedAmount: null as bigint | null,
startTime: null as bigint | null,
lastPull: null as bigint | null,
maxStreamLife: null as bigint | null,
reason: null as Uint8Array | null,
startTime: null as bigint | null,
token: null as string | null,
});

// Get info about your stream
Expand All @@ -96,13 +96,13 @@ const StreamPage = () => {
.streamInfo()
.then((info) => {
setStreamInfo({
token: info.token,
amountPerSecond: info.amount_per_second,
maxStreamLife: info.max_stream_life,
fundedAmount: info.funded_amount,
startTime: info.start_time,
lastPull: info.last_pull,
maxStreamLife: info.max_stream_life,
reason: info.reason,
startTime: info.start_time,
token: info.token,
});
})
.catch((error) => {
Expand Down
2 changes: 1 addition & 1 deletion ui/app/src/config.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
// Override this config in your downstream implementations as needed

const config = {
fromBlock: 4615000,
fromBlock: 4900000,
streamManagerAddress: "0x3543Faeeddb7bAbCbBB216B3627f9c5E0C39CE41",
tokens: [
{
Expand Down
96 changes: 81 additions & 15 deletions ui/lib/CreateStream.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import {
useContractWrite,
useWaitForTransaction,
useNetwork,
useContractRead,
} from "wagmi";
import { fetchBalance } from "@wagmi/core";
import StreamManager, { Stream } from "@apeworx/apepay";
Expand All @@ -23,6 +24,7 @@ export interface CreateStreamProps {
tokenList: TokenInfo[];
amountPerSecond: bigint;
cart?: ReactNode;
productName?: string;
registerStream: (stream: Stream) => void;
renderReasonCode: () => Promise<string>;
handleTransactionStatus: (
Expand Down Expand Up @@ -246,12 +248,60 @@ const CreateStream = (props: CreateStreamProps) => {
}
}, [props.tokenList, targetChainId]);

const [isAllowanceSufficient, setIsAllowanceSufficient] =
useState<boolean>(false);

// ABI used to fetch the current user allowance
const erc20ABI = [
{
constant: true,
inputs: [
{ name: "_owner", type: "address" },
{ name: "_spender", type: "address" },
],
name: "allowance",
outputs: [{ name: "", type: "uint256" }],
type: "function",
},
];

// Fetch current user allowance
const { data: allowanceData } = useContractRead({
address: selectedToken?.address as Address,
functionName: "allowance",
abi: erc20ABI,
args: [address, props.streamManagerAddress],
watch: true,
onError(error) {
console.log("Error fetching allowance", error);
},
onSettled(data, error) {
console.log("Allowance settled", { data, error });
},
});

useEffect(() => {
// Check if allowance data is available and update allowance state
if (allowanceData !== null && allowanceData !== undefined) {
const fetchedAllowance = Number(allowanceData.toString());

// Check if the fetched allowance is sufficient for the transaction cost
if (txCost !== undefined) {
setIsAllowanceSufficient(fetchedAllowance >= txCost);
}
}
}, [allowanceData, txCost]);

// Select the payment token among tokens with the same chainID
const Step1 = () => {
return (
<div>
<div className="cart-body">{props.cart && props.cart}</div>
<div className="payment-flow">
<div className="select-token-label">
{" "}
Select a token to pay for your {props.productName || "Stream"}{" "}
</div>
<select
className="select-token-dropdown"
value={
Expand Down Expand Up @@ -298,7 +348,7 @@ const CreateStream = (props: CreateStreamProps) => {
<div className="cart-body">{props.cart && props.cart}</div>
<div className="payment-flow">
<div className="fetching-sm-message">
Fetching stream manager address...
Fetching contract address...
</div>
</div>
</div>
Expand Down Expand Up @@ -391,6 +441,10 @@ const CreateStream = (props: CreateStreamProps) => {
<div>
<div className="cart-body">{props.cart && props.cart}</div>
<div className="payment-flow">
<div className="stream-duration">
Select the number of days you want to run your{" "}
{props.productName || "Stream"}
</div>
<Slider
className="slider-select-time"
min={1}
Expand All @@ -404,15 +458,27 @@ const CreateStream = (props: CreateStreamProps) => {
}
disabled={isSuccess}
/>
<button
className="button-validate-transaction"
onClick={approveStream}
disabled={isSuccess}
style={{ backgroundColor: isSuccess ? "grey" : "initial" }}
>
{`Approve ${Math.floor(transactionAmount + 1)}`}&nbsp;
{`${selectedToken?.symbol}`}
</button>
{isAllowanceSufficient ? (
<button
className="button-validate-transaction allowance"
onClick={createStream}
disabled={buttonCreateClicked}
>
{`Run ${props.productName || "Stream"} for ${
selectedTime / SECS_PER_DAY
} day${selectedTime !== SECS_PER_DAY ? "s" : ""}`}
</button>
) : (
<button
className="button-validate-transaction"
onClick={approveStream}
disabled={isSuccess}
style={{ backgroundColor: isSuccess ? "grey" : "initial" }}
>
{`Approve ${Math.floor(transactionAmount + 1)}`}{" "}
{`${selectedToken?.symbol}`}
</button>
)}
{isLoading && (
<div className="validate-transaction-message">
Waiting for your confirmation.
Expand All @@ -425,8 +491,8 @@ const CreateStream = (props: CreateStreamProps) => {
)}
{txLoading && (
<div className="validate-transaction-message">
Transaction approved: you will be redirected once it has been
processed...
Transaction approved: you will move to the next step once it has
been processed...
</div>
)}
{txError && (
Expand Down Expand Up @@ -454,9 +520,9 @@ const CreateStream = (props: CreateStreamProps) => {
onClick={createStream}
disabled={buttonCreateClicked}
>
{`Open Stream for ${selectedTime / SECS_PER_DAY} day${
selectedTime !== SECS_PER_DAY ? "s" : ""
}`}
{`Run ${props.productName || "Stream"} for ${
selectedTime / SECS_PER_DAY
} day${selectedTime !== SECS_PER_DAY ? "s" : ""}`}
</button>
</div>
</div>
Expand Down
70 changes: 65 additions & 5 deletions ui/lib/UpdateStream.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import { Stream } from "@apeworx/apepay";
import {
usePrepareContractWrite,
useContractWrite,
useContractRead,
useAccount,
useBalance,
} from "wagmi";
Expand Down Expand Up @@ -83,6 +84,50 @@ const UpdateStream: React.FC<UpdateStreamProps> = (props) => {
}
}, [isSuccess]);

const [isAllowanceSufficient, setIsAllowanceSufficient] =
useState<boolean>(false);

// ABI used to fetch the current user allowance
const erc20ABI = [
{
constant: true,
inputs: [
{ name: "_owner", type: "address" },
{ name: "_spender", type: "address" },
],
name: "allowance",
outputs: [{ name: "", type: "uint256" }],
type: "function",
},
];

// Fetch current user allowance
const { data: allowanceData } = useContractRead({
address: props.stream.token,
functionName: "allowance",
abi: erc20ABI,
args: [address, props.stream.streamManager.address],
watch: true,
onError(error) {
console.log("Error fetching allowance", error);
},
onSettled(data, error) {
console.log("Allowance settled", { data, error });
},
});

useEffect(() => {
// Check if allowance data is available and update allowance state
if (allowanceData !== null && allowanceData !== undefined) {
const fetchedAllowance = Number(allowanceData.toString());

// Check if the fetched allowance is sufficient for the transaction cost
if (contractAmount !== undefined) {
setIsAllowanceSufficient(fetchedAllowance >= contractAmount);
}
}
}, [allowanceData, contractAmount]);

// Step 1: set number of tokens you want to add
const Step1 = () => {
return (
Expand All @@ -102,11 +147,26 @@ const UpdateStream: React.FC<UpdateStreamProps> = (props) => {
typeof value === "number" && setSelectedTime(value)
}
/>
<button onClick={approveStream} className="update-stream-button">
{`Validate adding funds for ${selectedTime} additional day${
selectedTime !== 1 ? "s" : ""
}`}
</button>
{isAllowanceSufficient ? (
<button
className="update-stream-button allowance"
onClick={handleUpdate}
disabled={isButtonDisabled}
>
{`Add funds for ${selectedTime} additional day${
selectedTime !== 1 ? "s" : ""
}`}
</button>
) : (
<button
className="update-stream-button"
onClick={approveStream}
>
{`Validate adding funds for ${selectedTime} additional day${
selectedTime !== 1 ? "s" : ""
}`}
</button>
)}
</>
) : (
<>
Expand Down
Loading