Compare commits

..

28 Commits

Author SHA1 Message Date
Valtteri Karesto
4472957f5c Updated based on feedback 2022-05-27 10:43:16 +03:00
Valtteri Karesto
9a6ef2c393 Minor fixes based on feedback 2022-05-25 13:39:52 +03:00
Valtteri Karesto
56203ce9c6 Remove package-lock 2022-05-25 13:10:05 +03:00
Valtteri Karesto
933bdb5968 Add fee estimate button to fee and update the deploying 2022-05-25 13:05:04 +03:00
Valtteri Karesto
864711697b Update accounts 2022-05-25 13:03:49 +03:00
Valtteri Karesto
e5eaf09721 Just return the fee values, no mutating 2022-05-25 13:03:38 +03:00
Valtteri Karesto
d0dde56c67 Merge pull request #192 from XRPLF/fix/fix-sequence-number
Fix/fix sequence number
2022-05-24 18:20:31 +03:00
Valtteri Karesto
45c6927e72 Take next sequence number from response 2022-05-24 18:16:56 +03:00
Valtteri Karesto
6014b6e79f Update sequence after successful request 2022-05-24 18:14:01 +03:00
Valtteri Karesto
04a99227df Merge pull request #191 from XRPLF/fix/v2-updates
Fix/v2 updates
2022-05-24 15:44:00 +03:00
Valtteri Karesto
0965a1e898 Merge pull request #190 from XRPLF/feat/update-descriptions
Update meta tags
2022-05-24 15:43:47 +03:00
Valtteri Karesto
32445dbebf Mutate tx with fee estimation 2022-05-24 15:27:20 +03:00
Valtteri Karesto
1a1d4901aa Add estimate fee function 2022-05-24 15:26:38 +03:00
Valtteri Karesto
8b646c56dc Fix recursion 2022-05-24 15:26:03 +03:00
Valtteri Karesto
ac38bbc72c Turn the cleaner on by default 2022-05-24 15:25:33 +03:00
Valtteri Karesto
bf1182351a Added link to project 2022-05-23 13:28:41 +03:00
Valtteri Karesto
55e48a943b Update readme 2022-05-23 13:26:46 +03:00
Valtteri Karesto
faf417be69 Update meta tags 2022-05-23 08:55:03 +03:00
Valtteri Karesto
c2eb57211f hotfix/Remove debug code 2022-05-12 14:31:00 +03:00
Valtteri Karesto
0e97df3c8e Merge pull request #188 from eqlabs/fix/old-account-fix
Fixes to users who has old accounts
2022-05-12 08:35:02 +03:00
Valtteri Karesto
5dd0dfdc18 Fixes to users who has old accounts 2022-05-11 16:07:49 +03:00
Valtteri Karesto
ef48bac8f6 Merge pull request #187 from eqlabs/feat/fix-modal
Updated main modal
2022-05-11 14:31:53 +03:00
Valtteri Karesto
3a3d984098 Updated some copy 2022-05-11 14:22:48 +03:00
Valtteri Karesto
2300c201f8 Lift up modal a bit 2022-05-11 13:37:05 +03:00
Valtteri Karesto
329dc4a355 Remove unused import 2022-05-11 13:31:27 +03:00
Valtteri Karesto
cd6a5b23d4 Icons are now components so we can control the color 2022-05-11 13:30:02 +03:00
Valtteri Karesto
4dd7cbe2ca Updated main modal 2022-05-11 13:07:08 +03:00
Valtteri Karesto
260de7c838 Merge pull request #186 from eqlabs/fix/change-label
Added experimental to label
2022-05-11 10:02:03 +03:00
16 changed files with 782 additions and 12368 deletions

View File

@@ -1,6 +1,8 @@
# XRPL Hooks IDE
# XRPL Hooks Builder
This is the repository for XRPL Hooks IDE. This project is built with Next.JS
https://hooks-builder.xrpl.org/
This is the repository for XRPL Hooks Builder. This project is built with Next.JS
## General

View File

@@ -304,6 +304,18 @@ const Accounts: FC<AccountProps> = (props) => {
if (accountToUpdate) {
accountToUpdate.xrp = balance;
accountToUpdate.sequence = sequence;
accountToUpdate.error = null;
} else {
const oldAccount = state.accounts.find(
(acc) => acc.address === res?.account
);
if (oldAccount) {
oldAccount.xrp = "0";
oldAccount.error = {
code: res?.error,
message: res?.error_message,
};
}
}
});
const objectRequests = snap.accounts.map((acc) => {
@@ -343,7 +355,7 @@ const Accounts: FC<AccountProps> = (props) => {
}
};
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [snap.accounts, snap.clientStatus]);
}, [snap.accounts.length, snap.clientStatus]);
return (
<Box
as="div"
@@ -431,8 +443,9 @@ const Accounts: FC<AccountProps> = (props) => {
wordBreak: "break-word",
}}
>
{account.address} (
{Dinero({
{account.address}{" "}
{!account?.error ? (
`(${Dinero({
amount: Number(account?.xrp || "0"),
precision: 6,
})
@@ -441,8 +454,12 @@ const Accounts: FC<AccountProps> = (props) => {
style: "currency",
currency: "XRP",
currencyDisplay: "name",
})}
)
})})`
) : (
<Box css={{ color: "$red11" }}>
(Account not found, request funds to activate account)
</Box>
)}
</Text>
</Box>
{!props.hideDeployBtn && (
@@ -452,7 +469,7 @@ const Accounts: FC<AccountProps> = (props) => {
e.stopPropagation();
}}
>
<SetHookDialog account={account} />
<SetHookDialog accountAddress={account.address} />
</div>
)}
</Flex>

View File

@@ -40,6 +40,7 @@ const StyledContent = styled(DialogPrimitive.Content, {
color: "$mauve12",
borderRadius: "$md",
position: "relative",
mb: "15%",
boxShadow:
"0px 10px 38px -5px rgba(22, 23, 24, 0.25), 0px 10px 20px -5px rgba(22, 23, 24, 0.2)",
width: "90vw",

View File

@@ -28,6 +28,28 @@ import {
} from "./Dialog";
import PanelBox from "./PanelBox";
import { templateFileIds } from "../state/constants";
import { styled } from "../stitches.config";
import Starter from "../components/icons/Starter";
import Firewall from "../components/icons/Firewall";
import Notary from "../components/icons/Notary";
import Carbon from "../components/icons/Carbon";
import Peggy from "../components/icons/Peggy";
const ImageWrapper = styled(Flex, {
position: "relative",
mt: "$2",
mb: "$10",
svg: {
// fill: "red",
".angle": {
fill: "$text",
},
":not(.angle)": {
stroke: "$text",
},
},
});
const Navigation = () => {
const router = useRouter();
@@ -91,7 +113,7 @@ const Navigation = () => {
<Text
css={{ fontSize: "$xs", color: "$mauve10", lineHeight: 1 }}
>
{snap.files.length > 0 ? "Gist: " : "Playground"}
{snap.files.length > 0 ? "Gist: " : "Builder"}
{snap.files.length > 0 && (
<Link
href={`https://gist.github.com/${snap.gistOwner || ""}/${
@@ -128,19 +150,20 @@ const Navigation = () => {
</DialogTrigger>
<DialogContent
css={{
display: "flex",
maxWidth: "1080px",
width: "80vw",
height: "80%",
maxHeight: "80%",
backgroundColor: "$mauve1 !important",
overflowY: "auto",
background: "black",
p: 0,
}}
>
<Flex
css={{
flexDirection: "column",
flex: 1,
height: "auto",
height: "100%",
"@md": {
flexDirection: "row",
height: "100%",
@@ -151,15 +174,15 @@ const Navigation = () => {
css={{
borderBottom: "1px solid $colors$mauve5",
width: "100%",
minWidth: "240px",
flexDirection: "column",
p: "$7",
height: "100%",
backgroundColor: "$mauve2",
"@md": {
width: "30%",
maxWidth: "300px",
borderBottom: "0px",
borderRight: "1px solid $colors$mauve6",
borderRight: "1px solid $colors$mauve5",
},
}}
>
@@ -196,9 +219,9 @@ const Navigation = () => {
display: "inline-flex",
alignItems: "center",
gap: "$3",
color: "$purple10",
"&:hover": {
color: "$purple11",
"&:hover": {
color: "$purple12",
},
"&:focus": {
outline: 0,
@@ -217,9 +240,9 @@ const Navigation = () => {
display: "inline-flex",
alignItems: "center",
gap: "$3",
color: "$purple10",
"&:hover": {
color: "$purple11",
"&:hover": {
color: "$purple12",
},
"&:focus": {
outline: 0,
@@ -237,9 +260,9 @@ const Navigation = () => {
display: "inline-flex",
alignItems: "center",
gap: "$3",
color: "$purple10",
"&:hover": {
color: "$purple11",
"&:hover": {
color: "$purple12",
},
"&:focus": {
outline: 0,
@@ -255,7 +278,7 @@ const Navigation = () => {
</Flex>
</DialogDescription>
</Flex>
<div>
<Flex
css={{
display: "grid",
@@ -263,11 +286,16 @@ const Navigation = () => {
gridTemplateRows: "max-content",
flex: 1,
p: "$7",
pb: "$16",
gap: "$3",
alignItems: "normal",
flexWrap: "wrap",
backgroundColor: "$mauve1",
"@md": {
gridTemplateColumns: "1fr 1fr",
gridTemplateRows: "max-content",
},
"@lg": {
gridTemplateColumns: "1fr 1fr 1fr",
gridTemplateRows: "max-content",
},
@@ -277,15 +305,25 @@ const Navigation = () => {
as="a"
href={`/develop/${templateFileIds.starter}`}
>
<ImageWrapper>
<Starter />
</ImageWrapper>
<Heading>Starter</Heading>
<Text>
Just a basic starter with essential imports
Just a basic starter with essential imports, just
accepts any transaction coming through
</Text>
</PanelBox>
<PanelBox
as="a"
href={`/develop/${templateFileIds.firewall}`}
css={{ alignItems: "flex-start" }}
>
<ImageWrapper>
<Firewall />
</ImageWrapper>
<Heading>Firewall</Heading>
<Text>
This Hook essentially checks a blacklist of accounts
@@ -295,6 +333,9 @@ const Navigation = () => {
as="a"
href={`/develop/${templateFileIds.notary}`}
>
<ImageWrapper>
<Notary />
</ImageWrapper>
<Heading>Notary</Heading>
<Text>
Collecting signatures for multi-sign transactions
@@ -304,6 +345,9 @@ const Navigation = () => {
as="a"
href={`/develop/${templateFileIds.carbon}`}
>
<ImageWrapper>
<Carbon />
</ImageWrapper>
<Heading>Carbon</Heading>
<Text>Send a percentage of sum to an address</Text>
</PanelBox>
@@ -311,11 +355,13 @@ const Navigation = () => {
as="a"
href={`/develop/${templateFileIds.peggy}`}
>
<ImageWrapper>
<Peggy />
</ImageWrapper>
<Heading>Peggy</Heading>
<Text>An oracle based stable coin hook</Text>
</PanelBox>
</Flex>
</div>
</Flex>
<DialogClose asChild>
<Box

View File

@@ -21,11 +21,11 @@ import {
import { TTS, tts } from "../utils/hookOnCalculator";
import { deployHook } from "../state/actions";
import type { IAccount } from "../state";
import { useSnapshot } from "valtio";
import state from "../state";
import toast from "react-hot-toast";
import { sha256 } from "../state/actions/deployHook";
import { prepareDeployHookTx, sha256 } from "../state/actions/deployHook";
import estimateFee from "../utils/estimateFee";
const transactionOptions = Object.keys(tts).map((key) => ({
label: key,
@@ -37,6 +37,7 @@ export type SetHookData = {
value: keyof TTS;
label: string;
}[];
Fee: string;
HookNamespace: string;
HookParameters: {
HookParameter: {
@@ -52,8 +53,11 @@ export type SetHookData = {
// }[];
};
export const SetHookDialog: React.FC<{ account: IAccount }> = ({ account }) => {
export const SetHookDialog: React.FC<{ accountAddress: string }> = React.memo(
({ accountAddress }) => {
const snap = useSnapshot(state);
const account = snap.accounts.find((acc) => acc.address === accountAddress);
const [isSetHookDialogOpen, setIsSetHookDialogOpen] = useState(false);
const {
register,
@@ -61,16 +65,21 @@ export const SetHookDialog: React.FC<{ account: IAccount }> = ({ account }) => {
control,
watch,
setValue,
getValues,
formState: { errors },
} = useForm<SetHookData>({
defaultValues: {
HookNamespace: snap.files?.[snap.activeWat]?.name?.split(".")?.[0] || "",
HookNamespace:
snap.files?.[snap.activeWat]?.name?.split(".")?.[0] || "",
Invoke: transactionOptions.filter((to) => to.label === "ttPAYMENT"),
},
});
const { fields, append, remove } = useFieldArray({
control,
name: "HookParameters", // unique name for your Field Array
});
const [formInitialized, setFormInitialized] = useState(false);
const [estimateLoading, setEstimateLoading] = useState(false);
// Update value if activeWat changes
useEffect(() => {
@@ -78,6 +87,7 @@ export const SetHookDialog: React.FC<{ account: IAccount }> = ({ account }) => {
"HookNamespace",
snap.files?.[snap.activeWat]?.name?.split(".")?.[0] || ""
);
setFormInitialized(true);
}, [snap.activeWat, snap.files, setValue]);
// const {
// fields: grantFields,
@@ -100,6 +110,24 @@ export const SetHookDialog: React.FC<{ account: IAccount }> = ({ account }) => {
calculateHashedValue();
}, [namespace, calculateHashedValue]);
// Calcucate initial fee estimate when modal opens
useEffect(() => {
if (formInitialized && account) {
(async () => {
const formValues = getValues();
const tx = await prepareDeployHookTx(account, formValues);
if (!tx) {
return;
}
const res = await estimateFee(tx, account);
if (res && res.base_fee) {
setValue("Fee", res.base_fee);
}
})();
}
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [formInitialized]);
if (!account) {
return null;
}
@@ -118,7 +146,6 @@ export const SetHookDialog: React.FC<{ account: IAccount }> = ({ account }) => {
}
toast.error(`Transaction failed! (${res?.engine_result_message})`);
};
return (
<Dialog open={isSetHookDialogOpen} onOpenChange={setIsSetHookDialogOpen}>
<DialogTrigger asChild>
@@ -178,6 +205,7 @@ export const SetHookDialog: React.FC<{ account: IAccount }> = ({ account }) => {
<Input readOnly value={hashedNamespace} />
</Box>
</Box>
<Box css={{ width: "100%" }}>
<Label style={{ marginBottom: "10px", display: "block" }}>
Hook parameters
@@ -221,6 +249,69 @@ export const SetHookDialog: React.FC<{ account: IAccount }> = ({ account }) => {
</Button>
</Stack>
</Box>
<Box css={{ width: "100%", position: "relative" }}>
<Label>Fee</Label>
<Box css={{ display: "flex", alignItems: "center" }}>
<Input
type="number"
{...register("Fee", { required: true })}
autoComplete={"off"}
defaultValue={10000}
css={{
"-moz-appearance": "textfield",
"&::-webkit-outer-spin-button": {
"-webkit-appearance": "none",
margin: 0,
},
"&::-webkit-inner-spin-button ": {
"-webkit-appearance": "none",
margin: 0,
},
}}
/>
<Button
size="xs"
variant="primary"
outline
isLoading={estimateLoading}
css={{
position: "absolute",
right: "$2",
fontSize: "$xs",
cursor: "pointer",
alignContent: "center",
display: "flex",
}}
onClick={async (e) => {
e.preventDefault();
setEstimateLoading(true);
const formValues = getValues();
try {
const tx = await prepareDeployHookTx(
account,
formValues
);
if (tx) {
const res = await estimateFee(tx, account);
if (res && res.base_fee) {
setValue("Fee", res.base_fee);
}
}
} catch (err) {}
setEstimateLoading(false);
}}
>
Suggest
</Button>
</Box>
{errors.Fee?.type === "required" && (
<Box css={{ display: "inline", color: "$red11" }}>
Fee is required
</Box>
)}
</Box>
{/* <Box css={{ width: "100%" }}>
<label style={{ marginBottom: "10px", display: "block" }}>
Hook Grants
@@ -301,6 +392,9 @@ export const SetHookDialog: React.FC<{ account: IAccount }> = ({ account }) => {
</DialogContent>
</Dialog>
);
};
}
);
SetHookDialog.displayName = "SetHookDialog";
export default SetHookDialog;

View File

@@ -0,0 +1,40 @@
const Carbon = () => (
<svg
width="66"
height="32"
viewBox="0 0 66 32"
fill="none"
xmlns="http://www.w3.org/2000/svg"
>
<path
d="M33 2L23 15H28L21 24H45L38 15H43L33 2Z"
stroke="#EDEDEF"
strokeWidth="1.5"
strokeLinecap="round"
strokeLinejoin="round"
/>
<path
d="M33 24V30"
stroke="#EDEDEF"
strokeWidth="1.5"
strokeLinecap="round"
strokeLinejoin="round"
/>
<path
className="angle"
fillRule="evenodd"
clipRule="evenodd"
d="M-1.14441e-05 4L8.94099 15.0625L4.00543e-05 26.125H2.27587L10.5015 15.9475H16.5938V14.1775H10.5015L2.27582 4H-1.14441e-05Z"
fill="#EDEDEF"
/>
<path
className="angle"
fillRule="evenodd"
clipRule="evenodd"
d="M66 4L57.059 15.0625L66 26.125H63.7241L55.4985 15.9475H49.4062V14.1775H55.4985L63.7242 4H66Z"
fill="#EDEDEF"
/>
</svg>
);
export default Carbon;

View File

@@ -0,0 +1,75 @@
const Firewall = () => (
<svg
width="66"
height="32"
viewBox="0 0 66 32"
fill="none"
xmlns="http://www.w3.org/2000/svg"
>
<path
d="M33 13V7"
stroke="#EDEDEF"
strokeWidth="1.5"
strokeLinecap="round"
strokeLinejoin="round"
/>
<path
d="M27 19V13"
stroke="#EDEDEF"
strokeWidth="1.5"
strokeLinecap="round"
strokeLinejoin="round"
/>
<path
d="M39 19V13"
stroke="#EDEDEF"
strokeWidth="1.5"
strokeLinecap="round"
strokeLinejoin="round"
/>
<path
d="M33 25V19"
stroke="#EDEDEF"
strokeWidth="1.5"
strokeLinecap="round"
strokeLinejoin="round"
/>
<path
d="M21 13H45"
stroke="#EDEDEF"
strokeWidth="1.5"
strokeLinecap="round"
strokeLinejoin="round"
/>
<path
d="M21 19H45"
stroke="#EDEDEF"
strokeWidth="1.5"
strokeLinecap="round"
strokeLinejoin="round"
/>
<path
d="M45 7H21V25H45V7Z"
stroke="#EDEDEF"
strokeWidth="1.5"
strokeLinecap="round"
strokeLinejoin="round"
/>
<path
className="angle"
fillRule="evenodd"
clipRule="evenodd"
d="M-1.14441e-05 4.875L8.94099 15.9375L4.00543e-05 27H2.27587L10.5015 16.8225H16.5938V15.0525H10.5015L2.27582 4.875H-1.14441e-05Z"
fill="#EDEDEF"
/>
<path
className="angle"
fillRule="evenodd"
clipRule="evenodd"
d="M66 4.875L57.059 15.9375L66 27H63.7241L55.4985 16.8225H49.4062V15.0525H55.4985L63.7242 4.875H66Z"
fill="#EDEDEF"
/>
</svg>
);
export default Firewall;

View File

@@ -0,0 +1,40 @@
const Notary = () => (
<svg
width="66"
height="32"
viewBox="0 0 66 32"
fill="none"
xmlns="http://www.w3.org/2000/svg"
>
<path
d="M37.5 10.5L26.5 21.5L21 16.0002"
stroke="#EDEDEF"
strokeWidth="1.5"
strokeLinecap="round"
strokeLinejoin="round"
/>
<path
d="M49 10.5L38 21.5L35.0784 18.5785"
stroke="#EDEDEF"
strokeWidth="1.5"
strokeLinecap="round"
strokeLinejoin="round"
/>
<path
className="angle"
fillRule="evenodd"
clipRule="evenodd"
d="M-1.14441e-05 5L8.94099 16.0625L4.00543e-05 27.125H2.27587L10.5015 16.9475H16.5938V15.1775H10.5015L2.27582 5H-1.14441e-05Z"
fill="#EDEDEF"
/>
<path
className="angle"
fillRule="evenodd"
clipRule="evenodd"
d="M66 5L57.059 16.0625L66 27.125H63.7241L55.4985 16.9475H49.4062V15.1775H55.4985L63.7242 5H66Z"
fill="#EDEDEF"
/>
</svg>
);
export default Notary;

View File

@@ -0,0 +1,61 @@
const Peggy = () => (
<svg
width="66"
height="32"
viewBox="0 0 66 32"
fill="none"
xmlns="http://www.w3.org/2000/svg"
>
<path
d="M33 19C40.1797 19 46 16.3137 46 13C46 9.68629 40.1797 7 33 7C25.8203 7 20 9.68629 20 13C20 16.3137 25.8203 19 33 19Z"
stroke="#EDEDEF"
strokeWidth="1.5"
strokeLinecap="round"
strokeLinejoin="round"
/>
<path
d="M33 19V25"
stroke="#EDEDEF"
strokeWidth="1.5"
strokeLinecap="round"
strokeLinejoin="round"
/>
<path
d="M20 13V19C20 22 25 25 33 25C41 25 46 22 46 19V13"
stroke="#EDEDEF"
strokeWidth="1.5"
strokeLinecap="round"
strokeLinejoin="round"
/>
<path
d="M41 17.7633V23.7634"
stroke="#EDEDEF"
strokeWidth="1.5"
strokeLinecap="round"
strokeLinejoin="round"
/>
<path
d="M25 17.7633V23.7634"
stroke="#EDEDEF"
strokeWidth="1.5"
strokeLinecap="round"
strokeLinejoin="round"
/>
<path
className="angle"
fillRule="evenodd"
clipRule="evenodd"
d="M-1.14441e-05 4L8.94099 15.0625L4.00543e-05 26.125H2.27587L10.5015 15.9475H16.5938V14.1775H10.5015L2.27582 4H-1.14441e-05Z"
fill="#EDEDEF"
/>
<path
className="angle"
fillRule="evenodd"
clipRule="evenodd"
d="M66 4L57.059 15.0625L66 26.125H63.7241L55.4985 15.9475H49.4062V14.1775H55.4985L63.7242 4H66Z"
fill="#EDEDEF"
/>
</svg>
);
export default Peggy;

View File

@@ -0,0 +1,40 @@
const Starter = () => (
<svg
width="66"
height="32"
viewBox="0 0 66 32"
fill="none"
xmlns="http://www.w3.org/2000/svg"
>
<path
d="M42 28H24C23.7347 28 23.4804 27.8946 23.2929 27.7071C23.1053 27.5196 23 27.2652 23 27V5C23 4.73479 23.1053 4.48044 23.2929 4.2929C23.4804 4.10537 23.7347 4.00001 24 4H36.0003L43 11V27C43 27.2652 42.8947 27.5196 42.7071 27.7071C42.5196 27.8946 42.2653 28 42 28V28Z"
stroke="#EDEDEF"
strokeWidth="1.5"
strokeLinecap="round"
strokeLinejoin="round"
/>
<path
d="M36 4V11H43.001"
stroke="#EDEDEF"
strokeWidth="1.5"
strokeLinecap="round"
strokeLinejoin="round"
/>
<path
className="angle"
fillRule="evenodd"
clipRule="evenodd"
d="M-1.14441e-05 4.875L8.94099 15.9375L4.00543e-05 27H2.27587L10.5015 16.8225H16.5938V15.0525H10.5015L2.27582 4.875H-1.14441e-05Z"
fill="#EDEDEF"
/>
<path
className="angle"
fillRule="evenodd"
clipRule="evenodd"
d="M66 4.875L57.059 15.9375L66 27H63.7241L55.4985 16.8225H49.4062V15.0525H55.4985L63.7242 4.875H66Z"
fill="#EDEDEF"
/>
</svg>
);
export default Starter;

12057
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@@ -16,7 +16,7 @@ import state from "../state";
import TimeAgo from "javascript-time-ago";
import en from "javascript-time-ago/locale/en.json";
import { useSnapshot } from "valtio";
import Alert from '../components/AlertDialog';
import Alert from "../components/AlertDialog";
TimeAgo.setDefaultLocale(en.locale);
TimeAgo.addLocale(en);
@@ -61,22 +61,22 @@ function MyApp({ Component, pageProps: { session, ...pageProps } }: AppProps) {
<meta name="format-detection" content="telephone=no" />
<meta property="og:url" content={`${origin}${router.asPath}`} />
<title>XRPL Hooks Editor</title>
<meta property="og:title" content="XRPL Hooks Editor" />
<meta name="twitter:title" content="XRPL Hooks Editor" />
<title>XRPL Hooks Builder</title>
<meta property="og:title" content="XRPL Hooks Builder" />
<meta name="twitter:title" content="XRPL Hooks Builder" />
<meta name="twitter:card" content="summary_large_image" />
<meta name="twitter:site" content="@xrpllabs" />
<meta name="twitter:site" content="@XRPLF" />
<meta
name="description"
content="Playground for buildings Hooks, that add smart contract functionality to the XRP Ledger."
content="Hooks Builder, add smart contract functionality to the XRP Ledger."
/>
<meta
property="og:description"
content="Playground for buildings Hooks, that add smart contract functionality to the XRP Ledger."
content="Hooks Builder, add smart contract functionality to the XRP Ledger."
/>
<meta
name="twitter:description"
content="Playground for buildings Hooks, that add smart contract functionality to the XRP Ledger.."
content="Hooks Builder, add smart contract functionality to the XRP Ledger."
/>
<meta property="og:image" content={`${origin}${shareImg}`} />
<meta property="og:image:width" content="1200" />
@@ -101,7 +101,7 @@ function MyApp({ Component, pageProps: { session, ...pageProps } }: AppProps) {
/>
<link rel="manifest" href="/site.webmanifest" />
<link rel="mask-icon" href="/safari-pinned-tab.svg" color="#161618" />
<meta name="application-name" content="XRPL Hooks Editor" />
<meta name="application-name" content="XRPL Hooks Builder" />
<meta name="msapplication-TileColor" content="#c10ad0" />
<meta
name="theme-color"

View File

@@ -6,13 +6,14 @@ import calculateHookOn, { TTS } from "../../utils/hookOnCalculator";
import { SetHookData } from "../../components/SetHookDialog";
import { Link } from "../../components";
import { ref } from "valtio";
import estimateFee from "../../utils/estimateFee";
export const sha256 = async (string: string) => {
const utf8 = new TextEncoder().encode(string);
const hashBuffer = await crypto.subtle.digest("SHA-256", utf8);
const hashArray = Array.from(new Uint8Array(hashBuffer));
const hashHex = hashArray
.map(bytes => bytes.toString(16).padStart(2, "0"))
.map((bytes) => bytes.toString(16).padStart(2, "0"))
.join("");
return hashHex;
};
@@ -49,11 +50,7 @@ function arrayBufferToHex(arrayBuffer?: ArrayBuffer | null) {
return result;
}
/* deployHook function turns the wasm binary into
* hex string, signs the transaction and deploys it to
* Hooks testnet.
*/
export const deployHook = async (
export const prepareDeployHookTx = async (
account: IAccount & { name?: string },
data: SetHookData
) => {
@@ -72,12 +69,12 @@ export const deployHook = async (
return;
}
const HookNamespace = (await sha256(data.HookNamespace)).toUpperCase();
const hookOnValues: (keyof TTS)[] = data.Invoke.map(tt => tt.value);
const hookOnValues: (keyof TTS)[] = data.Invoke.map((tt) => tt.value);
const { HookParameters } = data;
const filteredHookParameters = HookParameters.filter(
hp =>
(hp) =>
hp.HookParameter.HookParameterName && hp.HookParameter.HookParameterValue
)?.map(aa => ({
)?.map((aa) => ({
HookParameter: {
HookParameterName: toHex(aa.HookParameter.HookParameterName || ""),
HookParameterValue: aa.HookParameter.HookParameterValue || "",
@@ -92,13 +89,12 @@ export const deployHook = async (
// }
// }
// });
if (typeof window !== "undefined") {
const tx = {
Account: account.address,
TransactionType: "SetHook",
Sequence: account.sequence,
Fee: "100000",
Fee: data.Fee,
Hooks: [
{
Hook: {
@@ -117,18 +113,39 @@ export const deployHook = async (
},
],
};
return tx;
}
};
/* deployHook function turns the wasm binary into
* hex string, signs the transaction and deploys it to
* Hooks testnet.
*/
export const deployHook = async (
account: IAccount & { name?: string },
data: SetHookData
) => {
if (typeof window !== "undefined") {
const tx = await prepareDeployHookTx(account, data);
if (!tx) {
return;
}
if (!state.client) {
return;
}
const keypair = derive.familySeed(account.secret);
const { signedTransaction } = sign(tx, keypair);
const currentAccount = state.accounts.find(
acc => acc.address === account.address
(acc) => acc.address === account.address
);
if (currentAccount) {
currentAccount.isLoading = true;
}
let submitRes;
try {
submitRes = await state.client.send({
submitRes = await state.client?.send({
command: "submit",
tx_blob: signedTransaction,
});
@@ -183,7 +200,7 @@ export const deleteHook = async (account: IAccount & { name?: string }) => {
return;
}
const currentAccount = state.accounts.find(
acc => acc.address === account.address
(acc) => acc.address === account.address
);
if (currentAccount?.isLoading || !currentAccount?.hooks.length) {
return;
@@ -205,6 +222,14 @@ export const deleteHook = async (account: IAccount & { name?: string }) => {
};
const keypair = derive.familySeed(account.secret);
try {
// Update tx Fee value with network estimation
const res = await estimateFee(tx, account);
tx["Fee"] = res?.base_fee ? res?.base_fee : "1000";
} catch (err) {
// use default value what you defined earlier
console.log(err);
}
const { signedTransaction } = sign(tx, keypair);
if (currentAccount) {

View File

@@ -24,10 +24,7 @@ export const sendTransaction = async (account: IAccount, txOptions: TransactionO
Fee, // TODO auto-fillable
...opts
};
const currAcc = state.accounts.find(acc => acc.address === account.address);
if (currAcc) {
currAcc.sequence = account.sequence + 1;
}
const { logPrefix = '' } = options || {}
try {
const signedAccount = derive.familySeed(account.secret);
@@ -47,6 +44,10 @@ export const sendTransaction = async (account: IAccount, txOptions: TransactionO
message: `${logPrefix}[${response.error || response.engine_result}] ${response.error_exception || response.engine_result_message}`,
});
}
const currAcc = state.accounts.find(acc => acc.address === account.address);
if (currAcc && response.account_sequence_next) {
currAcc.sequence = response.account_sequence_next;
}
} catch (err) {
console.error(err);
state.transactionLogs.push({

View File

@@ -35,6 +35,10 @@ export interface IAccount {
hooks: string[];
isLoading: boolean;
version?: string;
error?: {
message: string;
code: string;
} | null;
}
export interface ILog {
@@ -109,7 +113,7 @@ let initialState: IState = {
accounts: [],
compileOptions: {
optimizationLevel: '-O0',
strip: false
strip: true
}
};

25
utils/estimateFee.ts Normal file
View File

@@ -0,0 +1,25 @@
import { derive, sign } from "xrpl-accountlib"
import state, { IAccount } from "../state"
const estimateFee = async (tx: Record<string, unknown>, account: IAccount): Promise<null | { base_fee: string, median_fee: string; minimum_fee: string; open_ledger_fee: string; }> => {
const copyTx = JSON.parse(JSON.stringify(tx))
delete copyTx['SigningPubKey']
if (!copyTx.Fee) {
copyTx.Fee = '1000'
}
const keypair = derive.familySeed(account.secret)
const { signedTransaction } = sign(copyTx, keypair);
try {
const res = await state.client?.send({ command: 'fee', tx_blob: signedTransaction })
if (res && res.drops) {
return res.drops;
}
return null
} catch (err) {
console.log(err)
return null
}
}
export default estimateFee