Skip to content

Commit

Permalink
feat: Add Substrate dripping in the faucet (#147)
Browse files Browse the repository at this point in the history
* feat: Faucet update to support Substrate dripping

* Modify url

* Update condition check

* Insert faucet UI in examples

* Correct the instruction in the example

* Align with new REST endpoints

* Reseting states fix

* Update faucet path
  • Loading branch information
mj52951 authored Jun 6, 2024
1 parent 5424d72 commit 09747aa
Show file tree
Hide file tree
Showing 7 changed files with 105 additions and 30 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,12 @@ Before running the script, ensure that you have the following:
- The 12-word mnemonic for your Substrate development wallet
- An Ethereum wallet to receive tokens into (the example presets an existing wallet address already)
- A Substrate provider (in case the hardcoded WSS within the script does not work)
- A Substrate development wallet funded with `sygUSD` tokens; you will need to run the [EVM-to-Substrate example](02-EVM-Substrate-example.md) first to preload `sygUSD` tokens into a Substrate wallet
- A Substrate development wallet funded with `sygUSD` tokens

import App from '../../../../src/Faucet/App';

<App />
<br/>

:::danger
We make use of the dotenv module to manage Substrate's private mnemonics with environment variables. Please note that accidentally committing a .env file containing private mnemonics to a wallet with real funds, onto GitHub, could result in the complete loss of your funds. **Never expose your private keys.**
Expand Down
54 changes: 43 additions & 11 deletions src/Faucet/App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -17,16 +17,20 @@ import TokenInfo from "./components/TokenInfo";
import TxHashLabel from "./components/TxHashLabel";
import ErrorDialog from "./components/ErrorDialog";
import Progress from "./components/Progress";
import NetworkSelect from "./components/NetworkSelect";

import { getDomains, getTokens, mintRequest } from "./services/ConfigService";

import { sygmaTheme } from "./themes/SygmaTheme";
import { Domain, Network } from "@buildwithsygma/sygma-sdk-core";
import { capName } from "../utils";

// Timeout interval for timer
const TIMEOUT = 60 //seconds

const BASE_URL = "https://faucet-api-stage.buildwithsygma.com"

function App() {
const [domains, setDomains] = useState([]);
const [tokens, setTokens] = useState([]);
const [sDomain, setsDomain] = useState<any>();
const [sToken, setsToken] = useState<any>({});
Expand All @@ -37,14 +41,26 @@ function App() {
const [serverError, setServerError] = useState<any>();
const [expiredDate, setExpiredDate] = useState(Cookies.get('mintedExpires'));
const [progress, setProgress] = useState(0);

const [evmDomains, setEvmDomains] = useState([]);
const [substrateDomains, setSubstrateDomains] = useState([]);
const [selectedNetworkType, setSelectedNetworkType] = useState("");

useEffect(() => {
const timeout = Cookies.get('mintedExpires')
setExpiredDate(timeout)
getDomains("https://faucet-api-stage.buildwithsygma.com").then(
getDomains(BASE_URL).then(
(domains) => {
setDomains(domains.domains);
let evmDomains: Domain[] = [];
let substrateDomains: Domain[] = [];
Object.values(domains.domains).forEach((domain: Domain) => {
if (domain.type === Network.EVM){
evmDomains.push(domain)
} else if (domain.type === Network.SUBSTRATE){
substrateDomains.push(domain)
}
})
setEvmDomains(evmDomains);
setSubstrateDomains(substrateDomains);
}
);
}, []);
Expand Down Expand Up @@ -73,7 +89,7 @@ function App() {
const setSelectedDomain = (selectedDomain) => {
setsDomain(selectedDomain);
getTokens(
"https://faucet-api-stage.buildwithsygma.com",
BASE_URL,
selectedDomain.id
).then((tokens) => {
setTokens(tokens.tokens);
Expand All @@ -90,6 +106,13 @@ function App() {
setsToAddress(event.target.value);
};

const handleNetworkTypeChange = (networkType) => {
setSelectedNetworkType(networkType);
setsDomain(undefined);
setTokens([]);
setsToken({});
};

const mint = (e) => {
e.preventDefault();
setsMinting(true);
Expand All @@ -98,9 +121,9 @@ function App() {
expires: timeToExpire,
});
mintRequest(
"https://faucet-api-stage.buildwithsygma.com",
BASE_URL,
sDomain.id,
sToken.address,
sToken.resourceID,
toAddress
)
.then((respo) => {
Expand All @@ -120,6 +143,8 @@ function App() {
return sDomain !== undefined && sToken.address !== undefined && toAddress !== "";
};

const filteredDomains = selectedNetworkType.toLocaleLowerCase() === Network.EVM ? evmDomains : substrateDomains;

return (
<div>
<ThemeProvider theme={sygmaTheme}>
Expand All @@ -130,22 +155,29 @@ function App() {
onSubmit={mint}
>
<Grid container spacing={2}>
<Grid item xs={12} sm={6}>
<NetworkSelect
networkTypes={[Network.EVM.toLocaleUpperCase(), capName(Network.SUBSTRATE)]}
setSelectedNetworkType={handleNetworkTypeChange}/>
</Grid>
<Grid item xs={12} sm={6}>
<DomainSelect
disabled={minting}
domainArray={domains}
disabled={minting || !selectedNetworkType}
domainArray={filteredDomains}
setSelectedDomain={setSelectedDomain}
selectedDomain={sDomain}
/>
</Grid>
<Grid item xs={12} sm={6}>
<Grid item xs={12} sm={12}>
<TokenSelect
disabled={!sDomain || minting}
tokenArray={tokens}
setSelectedToken={setSelectedToken}
selectedToken={sToken}
/>
</Grid>

{sToken.address && <TokenInfo tokenInfo={sToken} />}
{sToken.address && <TokenInfo tokenInfo={sToken} domainType={selectedNetworkType.toLocaleLowerCase()} />}

<Grid item xs={12} sm={12}>
<FormControl fullWidth>
Expand Down
7 changes: 2 additions & 5 deletions src/Faucet/components/DomainSelect.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,8 @@ import MenuItem from "@mui/material/MenuItem";
import FormControl from "@mui/material/FormControl";
import Select from "@mui/material/Select";

export default function DomainSelect({ domainArray, setSelectedDomain, disabled }) {
const [domain, setDomain] = React.useState("");

export default function DomainSelect({ domainArray, setSelectedDomain, disabled, selectedDomain}) {
const handleChange = (event) => {
setDomain(event.target.value);
setSelectedDomain(event.target.value);
};

Expand All @@ -21,7 +18,7 @@ export default function DomainSelect({ domainArray, setSelectedDomain, disabled
disabled={disabled}
labelId="domain-select-label"
id="domain-select"
value={domain}
value={selectedDomain || ""}
label="Network"
onChange={handleChange}
required
Expand Down
37 changes: 37 additions & 0 deletions src/Faucet/components/NetworkSelect.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
import * as React from "react";
import Box from "@mui/material/Box";
import InputLabel from "@mui/material/InputLabel";
import MenuItem from "@mui/material/MenuItem";
import FormControl from "@mui/material/FormControl";
import Select from "@mui/material/Select";

export default function NetworkSelect({ networkTypes, setSelectedNetworkType }) {
const [network, setNetwork] = React.useState("");

const handleChange = (event) => {
setSelectedNetworkType(event.target.value);
setNetwork(event.target.value);
};

return (
<Box sx={{ minWidth: 120, marginTop: 2, marginBottom: 2 }}>
<FormControl fullWidth>
<InputLabel id="network-select-label">Network Type</InputLabel>
<Select
labelId="network-type-label"
id="network-type-select"
value={network}
label="Network Type"
onChange={handleChange}
required
sx={{ textTransform: "capitalize" }}
>

{networkTypes.map((networkType) => (
<MenuItem key={networkType} value={networkType}>{networkType}</MenuItem>
))}
</Select>
</FormControl>
</Box>
);
}
8 changes: 5 additions & 3 deletions src/Faucet/components/TokenInfo.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,16 +2,18 @@ import * as React from "react";
import FormControl from "@mui/material/FormControl";
import TextField from "@mui/material/TextField";
import Grid from "@mui/material/Grid";
import { capName } from "@site/src/utils";
import { Network } from "@buildwithsygma/sygma-sdk-core";

export default function TokenInfo({ tokenInfo }) {
export default function TokenInfo({ tokenInfo, domainType }) {
return (
<>
<Grid item xs={12} sm={12}>
<FormControl fullWidth>
<TextField
disabled
id="contract-address"
label="Contract address"
label={domainType === Network.EVM ? "Contract address" : "Sender address"}
value={tokenInfo.address}
></TextField>
</FormControl>
Expand All @@ -22,7 +24,7 @@ export default function TokenInfo({ tokenInfo }) {
disabled
id="token-type"
label="Type"
value={tokenInfo.type.toUpperCase()}
value={domainType === Network.EVM ? tokenInfo.type.toUpperCase() : capName(tokenInfo.type)}
></TextField>
</FormControl>
</Grid>
Expand Down
7 changes: 2 additions & 5 deletions src/Faucet/components/TokenSelect.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,12 +5,9 @@ import MenuItem from '@mui/material/MenuItem';
import FormControl from '@mui/material/FormControl';
import Select from '@mui/material/Select';

export default function TokenSelect({tokenArray, setSelectedToken, disabled}) {

const [token, setToken] = React.useState('');
export default function TokenSelect({tokenArray, setSelectedToken, disabled, selectedToken}) {

const handleChange = (event) => {
setToken(event.target.value);
setSelectedToken(event.target.value);
};

Expand All @@ -21,7 +18,7 @@ export default function TokenSelect({tokenArray, setSelectedToken, disabled}) {
<Select
labelId="token-select-label"
id="token-select"
value={token}
value={selectedToken || ""}
label="Token"
onChange={handleChange}
disabled={disabled}
Expand Down
15 changes: 10 additions & 5 deletions src/Faucet/services/ConfigService.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ export async function getDomains(root) {

export async function getTokens(root, domainID) {
var resp = await axios({
url: root + "/tokens/" + domainID,
url: root + "/domains/" + domainID+ "/resources",
method: "GET"
})

Expand All @@ -26,12 +26,17 @@ export async function getTokens(root, domainID) {
}
}

export async function mintRequest(root, domain, token, to) {
export async function mintRequest(root, domain, resourceId, recipient){
var resp = await axios({
url: root + "/drip/" + domain + "/" + token + "/to/" + to,
method: "GET"
url: root + "/domains/" + domain + "/resources/" + resourceId+"/drip",
headers: {
'Content-Type': 'application/json'
},
data: {
recipient: recipient
},
method: "POST"
})

if(resp.status == 200) {
return resp.data;
} else {
Expand Down

0 comments on commit 09747aa

Please sign in to comment.