Skip to content

Instantly share code, notes, and snippets.

@mikedotexe
Created March 13, 2024 01:06
Show Gist options
  • Save mikedotexe/33ba89eb54f9f5b75f08a6dcfbf4dac0 to your computer and use it in GitHub Desktop.
Save mikedotexe/33ba89eb54f9f5b75f08a6dcfbf4dac0 to your computer and use it in GitHub Desktop.
Chain signatures (shell of BOS frontend)
const ProgressBar = VM.require("mike.near/widget/ProgressBar");
console.log('props', props);
const yoctoZeroes = "000000000000000000000000";
const [stakingAmount, setStakingAmount] = useState("6");
const [unstakingWithdrawlAmount, setUnstakingWithdrawlAmount] = useState("19");
const StakeUnstakeWithdraw = ({ validator, method, amount }) => {
const openAnotherModal = ({validator, method, amount}) => {
// Let's explicitly have these in a switch so no one tries any funny business with methods
switch (method) {
case 'stake':
Near.call(validator, "deposit_and_stake", { amount: `${stakingAmount}${yoctoZeroes}` }, null, amount);
break;
case 'unstake':
Near.call(validator, "unstake", { amount: unstakingWithdrawlAmount + yoctoZeroes });
break;
case 'withdraw':
Near.call(validator, "withdraw", { amount: unstakingWithdrawlAmount + yoctoZeroes });
break;
}
};
const handleCancel = () => {
// eventually add fade-out effect
setShowStakingModal(false);
};
const handleUnstake = (amount) => {
console.log("Unstaking…", amount);
openAnotherModal({validator, method, amount});
};
const handleStake = (amount) => {
console.log("Staking…", amount);
openAnotherModal({validator, method, amount});
};
const handleWithdraw = () => {
console.log("Withdrawing…", unstakingWithdrawlAmount);
openAnotherModal({validator, method, amount: unstakingWithdrawlAmount});
};
const containerStyle = {
boxShadow: "0 3px 6px rgba(0, 0, 0, 0.6)",
border: "3px solid #f2f1e9",
backgroundColor: "rgba(0, 0, 0, .7)",
background:
"radial-gradient(circle at center, rgba(255, 255, 255, 0.7), rgba(0, 0, 0, 0.7) 100%), radial-gradient(circle at 10% 90%, rgba(200, 200, 255, 0.8), transparent 60%),radial-gradient(circle at 50% 50%, rgba(151, 151, 255, 0.7), transparent 50%)",
padding: "20px",
width: "100%",
margin: "0 auto",
borderRadius: "13px",
overflow: "auto",
display: "flex",
flexDirection: "column",
transition: "opacity 0.3s ease-out",
};
const titleStyle = {
textAlign: "center",
color: "#f2f1e9",
fontWeight: "bold",
marginBottom: "6px",
textShadow: `
-1px -1px 0 #000,
1px -1px 0 #000,
-1px 1px 0 #000,
1px 1px 0 #000`,
letterSpacing: "1px",
};
return (
<div style={containerStyle}>
<h2 style={titleStyle}>{method} NEAR</h2>
{method == "stake" ? (
<input
value={stakingAmount}
onChange={(e) => setStakingAmount(e.target.value)}
style={{
padding: "13px",
width: "80%",
margin: "13px auto",
display: "block",
}}
/>
) : (
<input
value={unstakingWithdrawlAmount}
onChange={(e) => setUnstakingWithdrawlAmount(e.target.value)}
style={{
padding: "13px",
width: "80%",
margin: "13px auto",
display: "block",
}}
/>
)}
<button
onClick={(e) => {
// console.log('aloha method', method);
if (method === "unstake") {
handleUnstake(`${stakingAmount}${yoctoZeroes}`);
} else if (method === "stake") {
// Assuming you have a function handleStake for the "stake" action
handleStake(`${stakingAmount}${yoctoZeroes}`);
} else if (method === "withdraw") {
// handleWithdraw(`${stakingAmount}${yoctoZeroes}`);
handleWithdraw(`${stakingAmount}${yoctoZeroes}`);
}
}}
style={{padding: "13px", margin: "13px auto", display: "block"}}
>
<span style={{
textTransform: "capitalize",
}}>{method}</span>
</button>
<button
onClick={handleCancel}
style={{padding: "13px", margin: "13px auto", display: "block"}}
>
Cancel
</button>
</div>
);
};
// Interesting, this seems to need to be above the StakeUnstakeWithdrawModal declaration
const walletUnstake = ({ validator, amount }) => {
setStakingModalData({ validator, method: "unstake", amount });
setShowStakingModal(true);
};
const walletWithdraw = ({ validator, amount }) => {
console.log('aloha walletWithdraw amount', amount);
setStakingModalData({ validator, method: "withdraw" });
setShowStakingModal(true);
};
const walletStake = ({ validator, amount }) => {
console.log('aloha stake validator', validator)
setStakingModalData({ validator, method: "stake", amount });
setShowStakingModal(true);
};
// State initialization
const [progressVal, setProgressVal] = useState(0);
const [progressMax, setProgressMax] = useState(1_000);
const [started, setStarted] = useState(false);
const [showProgressBar, setShowProgressBar] = useState(true);
const [validatorStakingDetails, setValidatorStakingDetails] = useState([]);
const [isLoading, setIsLoading] = useState(true);
const [showStakingModal, setShowStakingModal] = useState(false);
const [stakingModalData, setStakingModalData] = useState(false);
const YOCTO_DIGITS = 24;
// being modified from near-js utils
function cleanupAmount(amt) {
// Remove commas by splitting on them and joining the parts back together
return amt.split(",").join("").trim();
}
function trimLeadingZeroes(str) {
// Find the first non-zero character
let firstNonZeroIndex = 0;
while (firstNonZeroIndex < str.length && str[firstNonZeroIndex] === "0") {
firstNonZeroIndex++;
}
// Return the substring from the first non-zero character or '0' if all characters were zeroes
return firstNonZeroIndex === str.length
? "0"
: str.substring(firstNonZeroIndex);
}
function parseNearAmount(amt) {
if (!amt) {
return null;
}
amt = cleanupAmount(amt);
const split = amt.split(".");
const wholePart = split[0];
const fracPart = split[1] || "";
// Check for valid format
if (split.length > 2 || fracPart.length > YOCTO_DIGITS) {
throw new Error(`Cannot parse '${amt}' as NEAR amount`);
}
// Combine the whole part and the fractional part padded to NEAR_NOMINATION_EXP length
return trimLeadingZeroes(wholePart + fracPart.padEnd(YOCTO_DIGITS, "0"));
}
// end modified from near-js utils
// this is a strange function that'll help with removing the modal nicely
// seems to be somewhat a BOS-related piquant feature
useEffect(async () => {
if (progressVal >= progressMax) {
const timer = setTimeout(() => setIsLoading(false), 300);
return () => clearTimeout(timer);
}
}, [progressVal, progressMax]);
const LoadingModal = () => {
// This helps with the flash of red if it hasn't loaded yet
if (!!!ProgressBar) {
return <></>;
} else {
if (!!!context.accountId) {
console.log("(BOS variable) context.accountId not ready");
return;
}
return (
<div
style={{
position: "fixed",
top: "50%",
left: "50%",
transform: "translate(-50%, -150%)",
padding: "6px",
width: "19%",
height: "13%",
backgroundColor: "rgba(0, 0, 0, 0.2)",
backdropFilter: "blur(6px)",
display: isLoading ? "flex" : "none",
justifyContent: "center",
alignItems: "center",
borderRadius: "300px",
transition: "opacity 0.2s ease-out, background-color 0.2s ease",
opacity: isLoading ? 1 : 0,
zIndex: 999,
background: `linear-gradient(to bottom, rgba(255, 255, 255, 0) 0%, rgba(0, 0, 0, 0.2) 100%)`,
boxShadow: "0 8px 32px 0 rgba(31, 38, 135, 0.37)",
}}
>
<ProgressBar key={progressVal} value={progressVal} max={progressMax} />
</div>
);
}
};
// debug by commenting this badboy out to see it
const stakingModalDisplayStyles = {
display: showStakingModal ? "flex" : "none",
opacity: showStakingModal ? 1 : 0,
};
const StakeUnstakeWithdrawModal = () => {
if (!StakeUnstakeWithdraw) {
return <></>;
} else {
const handleOutsideClick = (e) => {
setShowStakingModal(false);
};
const outerDivStyle = {
position: "fixed",
top: "6%",
left: 0,
width: "100%",
backgroundImage: "radial-gradient(ellipse, rgba(0, 0, 0, 0.3) 66%, transparent 80%)",
display: "flex",
justifyContent: "center",
zIndex: 998,
cursor: "pointer",
...stakingModalDisplayStyles,
};
const innerDivStyle = {
cursor: "auto",
position: "relative",
width: "37%",
height: "auto",
overflowY: "hidden",
padding: "1%",
background: "radial-gradient(ellipse, rgba(151, 151, 255, .8) 19%, rgba(0, 0, 0, 0.8) 100%)",
borderRadius: "13px",
boxShadow: "0 0 15px rgba(255, 255, 255, 0.6), 0 0 20px rgba(255, 255, 255, 0.4), 0 0 25px rgba(255, 255, 255, 0.3), 0 0 30px rgba(255, 255, 255, 0.2), 0 0 35px rgba(255, 255, 255, 0.1), 0 0 40px rgba(255, 255, 255, 0.05)",
display: "flex",
flexDirection: "column",
justifyContent: "center",
};
return (
<div style={outerDivStyle} onClick={handleOutsideClick}>
<div style={innerDivStyle} onClick={(e) => e.stopPropagation()}>
<StakeUnstakeWithdraw
validator={stakingModalData.validator}
method={stakingModalData.method}
amount={stakingModalData.amount}
/>
</div>
</div>
);
}
};
// begin styles
const formContainerStyle = {
background: "radial-gradient(circle at top left, rgba(0, 0, 0, 0.6) 30%, rgba(0, 0, 0, 0.2) 70%)",
filter: "drop-shadow(3px 3px 3px rgba(255, 255, 255, 0.6))",
backdropFilter: "contrast(0.1)",
padding: "20px",
margin: "20px auto",
color: "#fff",
borderRadius: "6px",
boxShadow: "0 0 1px 0 #f2f1e9",
};
const leftSideStyle = {
display: "flex",
flexDirection: "column",
gap: "10px",
flex: "1 1 auto",
};
const formStyle = {
display: "flex",
alignItems: "flex-end",
justifyContent: "space-between",
gap: "20px",
};
const rightSideStyle = {
background: "#111",
borderRadius: "6px",
color: "#f2f1e9",
border: "none",
padding: "20px 20px 20px 20px",
display: "flex",
flexDirection: "row",
justifyContent: "space-between",
gap: "10px",
flex: "1 1 600px",
minHeight: "fit-content",
marginLeft: "20px",
};
const rightBoxStyle = {
flex: "1",
display: "flex",
flexDirection: "column",
justifyContent: "center",
alignItems: "center",
padding: "20px",
borderRadius: "6px",
background: "rgba(255, 255, 255, 0.1)",
border: "1px solid rgba(255, 255, 255, 0.2)",
};
const fieldStyle = {
background: "rgba(255, 255, 255, 0.1)",
border: "1px solid #f2f1e9",
borderRadius: "4px",
color: "#fff",
padding: "10px 15px",
};
const buttonStyle = {
background: "linear-gradient(to right, #9797ff 6%, rgba(151, 151, 255, 0.6) 100%)",
color: "#fff",
border: "none",
borderRadius: "4px",
padding: "10px 20px",
cursor: "pointer",
};
// end styles
return (
<>
{!context.accountId && (
<div style={{textAlign: "center", color: "white"}}>
<h1>Please login →</h1>
</div>
)}
<div style={{
position: "relative",
width: "100%",
filter: "contrast(166%)",
}}>
<div
style={{
fontFamily: "'Lucida Console', Monaco, monospace",
padding: "13px 16px",
minWidth: "475px",
maxWidth: "900px",
margin: "0 auto",
background: `radial-gradient(circle at top right, slategray, transparent 80%),
radial-gradient(circle at center, darkslategray, transparent 83%)`,
backgroundImage: 'url("data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0idXRmLTgiPz48c3ZnIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIgd2lkdGg9IjEwMCIgaGVpZ2h0PSIxMDAiPjxkZWZzPjxmaWx0ZXIgaWQ9InNsYXRlTm9pc2UiIHg9Ii01MCUiIHk9Ii01MCUiIHdpZHRoPSIyMDAlIiBoZWlnaHQ9IjIwMCUiPjxmZVR1cmJ1bGVuY2UgdHlwZT0iZnJhY3RhbE5vaXNlIiBiYXNlRnJlcXVlbmN5PSIxLjkiIG51bU9jdGF2ZXM9IjYiIHJlc3VsdD0ibm9pc2UiLz48ZmVHYXVzc2lhbkJsdXIgaW49Im5vaXNlIiBzdGREZXZpYXRpb249IjIiIHJlc3VsdD0iYmx1cnJlZE5vaXNlIi8+PGZlQ29tcG9uZW50VHJhbnNmZXIgaW49InNsYXRlTm9pc2UiPjxmZUZ1bmNSIHR5cGU9ImxpbmVhciIgc2xvcGU9IjAuOSIgaW50ZXJjZXB0PSIwLjA1Ii8+PGZlRnVuY0cgdHlwZT0ibGluZWFyIiBzbG9wZT0iMC45IiBpbnRlcmNlcHQ9IjAuMDUiLz48ZmVGdW5jQiB0eXBlPSJsaW5lYXIiIHNsb3BlPSIwLjk1IiBpbnRlcmNlcHQ9IjAuMSIvPjwvZmVDb21wb25lbnRUcmFuc2Zlcj48L2ZpbHRlcj48L2RlZnM+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsdGVyPSJ1cmwoI3NsYXRlTm9pc2UpIiBmaWxsPSJkYXJrZ3JheSIgLz48L3N2Zz4=")',
backgroundBlendMode: "hard-light",
borderRadius: "16px 16px 0 0",
filter: "saturate(6) grayscale(6%)",
}}
>
{/* todo: check for modals and stuff */}
<h1 style={{
textAlign: "center",
color: "#f2f1e9",
textShadow: `-1px -1px 0 #000,1px -1px 0 #000,-1px 1px 0 #000,3px 3px 3px #000`,
letterSpacing: "6px",
textTransform: "uppercase",
}}>Chain Signatures</h1>
<div style={formContainerStyle}>
<h3>Bitcoin</h3>
<div style={{
height: "6px",
background: "linear-gradient(to right, goldenrod, rgba(255, 255, 255, 0.3), goldenrod)",
filter: "saturate(13%)",
marginBottom: "13px",
}}></div>
<div style={formStyle}>
<div style={leftSideStyle}>
<label style={{color: "#fff"}}>HD Path</label>
<input style={fieldStyle} type="text" value="m/44'/0'/0'/0"/>
<label style={{color: "#fff"}}>NEAR Account</label>
<input style={fieldStyle} type="text" value={context.accountId} readOnly/>
</div>
<div style={rightSideStyle}>
<div style={rightBoxStyle}>
<h4 style={{margin: 0, padding: 0, color: "#fff"}}>Balance</h4>
<button style={buttonStyle} onClick={() => refreshBalance('bitcoin')}>Refresh</button>
</div>
<div style={rightBoxStyle}>
{/* Right side content, the second box */}
<p style={{margin: 0, padding: 0, color: "#fff"}}>Right side content</p>
</div>
</div>
</div>
</div>
<div style={formContainerStyle}>
<h3>Ethereum</h3>
<div style={{
height: "6px",
background: "linear-gradient(to right, blue, rgba(255, 255, 255, 0.3), blue)",
filter: "saturate(13%)",
marginBottom: "13px",
}}></div>
<div style={formStyle}>
<div style={leftSideStyle}>
<label style={{color: "#fff"}}>HD Path</label>
<input style={fieldStyle} type="text" value="m/44'/0'/0'/0"/>
<label style={{color: "#fff"}}>NEAR Account</label>
<input style={fieldStyle} type="text" value={context.accountId} readOnly/>
</div>
<div style={rightSideStyle}>
<div style={rightBoxStyle}>
<h4 style={{margin: 0, padding: 0, color: "#fff"}}>Balance</h4>
<button style={buttonStyle} onClick={() => refreshBalance('eth')}>Refresh</button>
</div>
<div style={rightBoxStyle}>
<p style={{margin: 0, padding: 0, color: "#fff"}}>Right side content</p>
</div>
</div>
</div>
</div>
<div style={formContainerStyle}>
<h3>Dogecoin</h3>
<div style={{
height: "6px",
background: "linear-gradient(to right, silver, rgba(255, 255, 255, 0.3), silver)",
filter: "saturate(13%)",
marginBottom: "13px",
}}></div>
<div style={formStyle}>
<div style={leftSideStyle}>
<label style={{color: "#fff"}}>HD Path</label>
<input style={fieldStyle} type="text" value="m/44'/0'/0'/0"/>
<label style={{color: "#fff"}}>NEAR Account</label>
<input style={fieldStyle} type="text" value={context.accountId} readOnly/>
</div>
<div style={rightSideStyle}>
<div style={rightBoxStyle}>
<h4 style={{margin: 0, padding: 0, color: "#fff"}}>Balance</h4>
<button style={buttonStyle} onClick={() => refreshBalance('doge')}>Refresh</button>
</div>
<div style={rightBoxStyle}>
<p style={{margin: 0, padding: 0, color: "#fff"}}>Right side content</p>
</div>
</div>
</div>
</div>
<div style={formContainerStyle}>
<h3>Celestia</h3>
<div style={{
height: "6px",
background: "linear-gradient(to right, #6633ff, rgba(255, 255, 255, 0.3), #6633ff)",
filter: "saturate(13%)",
marginBottom: "13px",
}}></div>
<div style={formStyle}>
<div style={leftSideStyle}>
<label style={{color: "#fff"}}>HD Path</label>
<input style={fieldStyle} type="text" value="m/44'/0'/0'/0"/>
<label style={{color: "#fff"}}>NEAR Account</label>
<input style={fieldStyle} type="text" value={context.accountId} readOnly/>
</div>
<div style={rightSideStyle}>
<div style={rightBoxStyle}>
<h4 style={{margin: 0, padding: 0, color: "#fff"}}>Balance</h4>
<button style={buttonStyle} onClick={() => refreshBalance('tia')}>Refresh</button>
</div>
<div style={rightBoxStyle}>
<p style={{margin: 0, padding: 0, color: "#fff"}}>Right side content</p>
</div>
</div>
</div>
</div>
</div>
</div>
</>
);
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment