diff --git a/packages/dev-frontend/.eslintrc.cjs b/packages/dev-frontend/.eslintrc.cjs
index 4020bcbf4..47fcb4803 100644
--- a/packages/dev-frontend/.eslintrc.cjs
+++ b/packages/dev-frontend/.eslintrc.cjs
@@ -1,14 +1,20 @@
module.exports = {
env: { browser: true, es2020: true },
extends: [
- 'eslint:recommended',
- 'plugin:@typescript-eslint/recommended',
- 'plugin:react-hooks/recommended',
+ "eslint:recommended",
+ "plugin:@typescript-eslint/recommended",
+ "plugin:react-hooks/recommended"
],
- parser: '@typescript-eslint/parser',
- parserOptions: { ecmaVersion: 'latest', sourceType: 'module' },
- plugins: ['react-refresh'],
+ parser: "@typescript-eslint/parser",
+ parserOptions: { ecmaVersion: "latest", sourceType: "module" },
+ plugins: ["react-refresh"],
rules: {
- 'react-refresh/only-export-components': 'warn',
- },
-}
+ // "react-refresh/only-export-components": "warn",
+ "react-hooks/exhaustive-deps": [
+ "warn",
+ {
+ additionalHooks: "useTransaction"
+ }
+ ]
+ }
+};
diff --git a/packages/dev-frontend/package.json b/packages/dev-frontend/package.json
index fe4c00a7b..b91af4de5 100644
--- a/packages/dev-frontend/package.json
+++ b/packages/dev-frontend/package.json
@@ -6,6 +6,7 @@
"type": "module",
"dependencies": {
"@ethersproject/abi": "5.7.0",
+ "@ethersproject/experimental": "5.7.0",
"@fortawesome/fontawesome-svg-core": "1.2.34",
"@fortawesome/free-regular-svg-icons": "5.15.2",
"@fortawesome/free-solid-svg-icons": "5.15.2",
@@ -13,7 +14,7 @@
"@liquity/chicken-bonds": "file:.yalc/@liquity/chicken-bonds",
"@metamask/eth-sig-util": "5.0.1",
"@tippyjs/react": "4.2.5",
- "connectkit": "1.2.4",
+ "connectkit": "^1.3.0",
"ethers": "5.7.2",
"lambert-w-function": "3.0.0",
"react": "17.0.1",
@@ -24,7 +25,7 @@
"react-router-dom": "5.2.0",
"recharts": "2.1.10",
"theme-ui": "0.6.0-canary.1544.5359f8a1e408a4dfeb74a9ae39688270286e534a.0",
- "wagmi": "0.12.13"
+ "wagmi": "^0.12.0"
},
"devDependencies": {
"@esbuild-plugins/node-modules-polyfill": "^0.2.2",
diff --git a/packages/dev-frontend/src/App.tsx b/packages/dev-frontend/src/App.tsx
index e9db13fa3..fd6a2cbdb 100644
--- a/packages/dev-frontend/src/App.tsx
+++ b/packages/dev-frontend/src/App.tsx
@@ -15,11 +15,7 @@ import theme from "./theme";
import { DisposableWalletProvider } from "./testUtils/DisposableWalletProvider";
import { LiquityFrontend } from "./LiquityFrontend";
import { AppLoader } from "./components/AppLoader";
-
-if (window.ethereum) {
- // Silence MetaMask warning in console
- Object.assign(window.ethereum, { autoRefreshOnNetworkChange: false });
-}
+import { useAsyncValue } from "./hooks/AsyncValue";
if (import.meta.env.REACT_APP_DEMO_MODE === "true") {
const ethereum = new DisposableWalletProvider(
@@ -37,21 +33,6 @@ getConfig().then(config => {
Object.assign(window, { config });
});
-const wagmiClient = createClient(
- getDefaultClient({
- appName: "Liquity",
- chains: [mainnet, goerli]
- })
-);
-
-// const EthersWeb3ReactProvider: React.FC = ({ children }) => {
-// return (
-// new BatchedWebSocketAugmentedWeb3Provider(provider)}>
-// {children}
-//
-// );
-// };
-
const UnsupportedMainnetFallback: React.FC = () => (
(
This app is for testing purposes only.
-
- Please change your network to Ropsten, Rinkeby, Kovan, Görli or Kiln.
-
+ Please change your network to Görli.
If you'd like to use the Liquity Protocol on mainnet, please pick a frontend{" "}
@@ -80,43 +59,57 @@ const UnsupportedMainnetFallback: React.FC = () => (
);
+const UnsupportedNetworkFallback: React.FC = () => (
+
+
+ Liquity is not deployed to this network.
+
+ Please switch to Ropsten, Rinkeby, Kovan, Görli or Kiln.
+
+);
+
const App = () => {
- const unsupportedNetworkFallback = (chainId: number) => (
-
-
- Liquity is not yet deployed to{" "}
- {chainId === 1 ? "mainnet" : "this network"}.
-
- Please switch to Ropsten, Rinkeby, Kovan, Görli or Kiln.
-
- );
+ const config = useAsyncValue(getConfig);
+ const loader = ;
return (
-
-
-
- }>
- }
- unsupportedNetworkFallback={unsupportedNetworkFallback}
- unsupportedMainnetFallback={}
- >
-
- } />
-
-
-
-
-
-
+
+ {config.loaded && (
+
+
+
+ }
+ unsupportedMainnetFallback={}
+ >
+
+
+
+
+
+
+
+ )}
+
);
};
diff --git a/packages/dev-frontend/src/components/Bonds/context/BondViewContext.tsx b/packages/dev-frontend/src/components/Bonds/context/BondViewContext.tsx
index e6734fbf6..fb06f226a 100644
--- a/packages/dev-frontend/src/components/Bonds/context/BondViewContext.tsx
+++ b/packages/dev-frontend/src/components/Bonds/context/BondViewContext.tsx
@@ -32,6 +32,7 @@ export type BondViewContextType = {
bLusdAmmBLusdBalance?: Decimal;
bLusdAmmLusdBalance?: Decimal;
statuses: BondTransactionStatuses;
+ isInfiniteBondApproved: boolean;
isSynchronizing: boolean;
getLusdFromFaucet: () => Promise;
simulatedProtocolInfo?: ProtocolInfo;
diff --git a/packages/dev-frontend/src/components/Bonds/context/BondViewProvider.tsx b/packages/dev-frontend/src/components/Bonds/context/BondViewProvider.tsx
index 2515e741e..7a64ad9e2 100644
--- a/packages/dev-frontend/src/components/Bonds/context/BondViewProvider.tsx
+++ b/packages/dev-frontend/src/components/Bonds/context/BondViewProvider.tsx
@@ -51,6 +51,7 @@ export const BondViewProvider: React.FC = props => {
const [stats, setStats] = useState();
const [protocolInfo, setProtocolInfo] = useState();
const [simulatedProtocolInfo, setSimulatedProtocolInfo] = useState();
+ const [isInfiniteBondApproved, setIsInfiniteBondApproved] = useState(false);
const [lpRewards, setLpRewards] = useState();
const [isLusdApprovedWithBlusdAmm, setIsLusdApprovedWithBlusdAmm] = useState(false);
const [isBLusdApprovedWithBlusdAmm, setIsBLusdApprovedWithBlusdAmm] = useState(false);
@@ -63,6 +64,7 @@ export const BondViewProvider: React.FC = props => {
BLusdAmmTokenIndex.BLUSD
);
const [statuses, setStatuses] = useState({
+ APPROVE: "IDLE",
CREATE: "IDLE",
CANCEL: "IDLE",
CLAIM: "IDLE",
@@ -141,16 +143,39 @@ export const BondViewProvider: React.FC = props => {
);
const getLusdFromFaucet = useCallback(async () => {
- if (contracts.lusdToken === undefined) return;
+ if (contracts.lusdToken === undefined || liquity.connection.signer === undefined) return;
+
if (
LUSD_OVERRIDE_ADDRESS !== null &&
(await contracts.lusdToken.balanceOf(account)).eq(0) &&
"tap" in contracts.lusdToken
) {
- await (await ((contracts.lusdToken as unknown) as ERC20Faucet).tap()).wait();
+ await (
+ await ((contracts.lusdToken as unknown) as ERC20Faucet)
+ .connect(liquity.connection.signer)
+ .tap()
+ ).wait();
setShouldSynchronize(true);
}
- }, [contracts.lusdToken, account, LUSD_OVERRIDE_ADDRESS]);
+ }, [contracts.lusdToken, account, LUSD_OVERRIDE_ADDRESS, liquity.connection.signer]);
+
+ useEffect(() => {
+ (async () => {
+ if (
+ contracts.lusdToken === undefined ||
+ contracts.chickenBondManager === undefined ||
+ account === undefined ||
+ isInfiniteBondApproved
+ )
+ return;
+ const isApproved = await api.isInfiniteBondApproved(
+ account,
+ contracts.lusdToken,
+ contracts.chickenBondManager
+ );
+ setIsInfiniteBondApproved(isApproved);
+ })();
+ }, [contracts.lusdToken, contracts.chickenBondManager, account, isInfiniteBondApproved]);
useEffect(() => {
(async () => {
@@ -324,25 +349,48 @@ export const BondViewProvider: React.FC = props => {
})();
}, [isSynchronizing, shouldSynchronize, account, contracts, simulatedProtocolInfo]);
+ const [approveInfiniteBond, approveStatus] = useTransaction(async () => {
+ await api.approveInfiniteBond(
+ contracts.lusdToken,
+ contracts.chickenBondManager,
+ liquity.connection.signer
+ );
+ setIsInfiniteBondApproved(true);
+ }, [contracts.lusdToken, contracts.chickenBondManager, liquity.connection.signer]);
+
const [approveAmm, approveAmmStatus] = useTransaction(
async (tokensNeedingApproval: BLusdAmmTokenIndex[]) => {
for (const token of tokensNeedingApproval) {
if (token === BLusdAmmTokenIndex.BLUSD) {
await (isMainnet
- ? api.approveTokenWithBLusdAmmMainnet(contracts.bLusdToken)
- : api.approveTokenWithBLusdAmm(contracts.bLusdToken, BLUSD_AMM_ADDRESS));
+ ? api.approveTokenWithBLusdAmmMainnet(contracts.bLusdToken, liquity.connection.signer)
+ : api.approveTokenWithBLusdAmm(
+ contracts.bLusdToken,
+ BLUSD_AMM_ADDRESS,
+ liquity.connection.signer
+ ));
setIsBLusdApprovedWithBlusdAmm(true);
} else {
await (isMainnet
- ? api.approveTokenWithBLusdAmmMainnet(contracts.lusdToken)
- : api.approveTokenWithBLusdAmm(contracts.lusdToken, BLUSD_AMM_ADDRESS));
+ ? api.approveTokenWithBLusdAmmMainnet(contracts.lusdToken, liquity.connection.signer)
+ : api.approveTokenWithBLusdAmm(
+ contracts.lusdToken,
+ BLUSD_AMM_ADDRESS,
+ liquity.connection.signer
+ ));
setIsLusdApprovedWithBlusdAmm(true);
}
}
},
- [contracts.bLusdToken, contracts.lusdToken, isMainnet, BLUSD_AMM_ADDRESS]
+ [
+ contracts.bLusdToken,
+ contracts.lusdToken,
+ isMainnet,
+ BLUSD_AMM_ADDRESS,
+ liquity.connection.signer
+ ]
);
const [approveTokens, approveTokensStatus] = useTransaction(
@@ -350,22 +398,26 @@ export const BondViewProvider: React.FC = props => {
if (contracts.bLusdAmm === undefined) return;
for (const [token, spender] of Array.from(tokensNeedingApproval)) {
if (token === BLusdAmmTokenIndex.BLUSD) {
- await api.approveToken(contracts.bLusdToken, spender);
+ await api.approveToken(contracts.bLusdToken, spender, liquity.connection.signer);
if (spender === BLUSD_AMM_ADDRESS) {
setIsBLusdApprovedWithBlusdAmm(true);
} else if (spender === BLUSD_LP_ZAP_ADDRESS) {
setIsBLusdApprovedWithAmmZapper(true);
}
} else if (token === BLusdAmmTokenIndex.LUSD) {
- await api.approveToken(contracts.lusdToken, BLUSD_LP_ZAP_ADDRESS);
+ await api.approveToken(
+ contracts.lusdToken,
+ BLUSD_LP_ZAP_ADDRESS,
+ liquity.connection.signer
+ );
setIsLusdApprovedWithAmmZapper(true);
} else if (token === BLusdAmmTokenIndex.BLUSD_LUSD_LP && spender === undefined) {
const lpToken = await api.getLpToken(contracts.bLusdAmm);
- await api.approveToken(lpToken, BLUSD_LP_ZAP_ADDRESS);
+ await api.approveToken(lpToken, BLUSD_LP_ZAP_ADDRESS, liquity.connection.signer);
setIsBLusdLpApprovedWithAmmZapper(true);
} else if (token === BLusdAmmTokenIndex.BLUSD_LUSD_LP) {
const lpToken = await api.getLpToken(contracts.bLusdAmm);
- await api.approveToken(lpToken, spender);
+ await api.approveToken(lpToken, spender, liquity.connection.signer);
if (spender === BLUSD_LP_ZAP_ADDRESS) {
setIsBLusdLpApprovedWithAmmZapper(true);
} else if (spender === BLUSD_AMM_STAKING_ADDRESS) {
@@ -381,20 +433,15 @@ export const BondViewProvider: React.FC = props => {
BLUSD_LP_ZAP_ADDRESS,
BLUSD_AMM_STAKING_ADDRESS,
BLUSD_AMM_ADDRESS,
- isBLusdApprovedWithAmmZapper,
- isLusdApprovedWithAmmZapper
+ liquity.connection.signer
]
);
const [createBond, createStatus] = useTransaction(
async (lusdAmount: Decimal) => {
- if (liquity.connection.signer === undefined) return;
-
await api.createBond(
lusdAmount,
account,
- LUSD_OVERRIDE_ADDRESS ?? liquity.connection.addresses.lusdToken,
- contracts.lusdToken,
contracts.chickenBondManager,
liquity.connection.signer
);
@@ -407,25 +454,31 @@ export const BondViewProvider: React.FC = props => {
setOptimisticBond(optimisticBond);
setShouldSynchronize(true);
},
- [contracts.chickenBondManager, contracts.lusdToken, liquity.connection.signer, account]
+ [contracts.chickenBondManager, liquity.connection.signer, account]
);
const [cancelBond, cancelStatus] = useTransaction(
async (bondId: string, minimumLusd: Decimal) => {
- await api.cancelBond(bondId, minimumLusd, contracts.chickenBondManager);
+ await api.cancelBond(
+ bondId,
+ minimumLusd,
+ account,
+ contracts.chickenBondManager,
+ liquity.connection.signer
+ );
removeBondFromList(bondId);
setShouldSynchronize(true);
},
- [contracts.chickenBondManager, removeBondFromList]
+ [contracts.chickenBondManager, removeBondFromList, liquity.connection.signer, account]
);
const [claimBond, claimStatus] = useTransaction(
async (bondId: string) => {
- await api.claimBond(bondId, contracts.chickenBondManager);
+ await api.claimBond(bondId, account, contracts.chickenBondManager, liquity.connection.signer);
changeBondStatusToClaimed(bondId);
setShouldSynchronize(true);
},
- [contracts.chickenBondManager, changeBondStatusToClaimed]
+ [contracts.chickenBondManager, changeBondStatusToClaimed, liquity.connection.signer, account]
);
const getExpectedSwapOutput = useCallback(
@@ -446,22 +499,22 @@ export const BondViewProvider: React.FC = props => {
inputToken,
inputAmount,
minOutputAmount,
- contracts.bLusdAmm
+ contracts.bLusdAmm,
+ liquity.connection.signer,
+ account
);
setShouldSynchronize(true);
},
- [contracts.bLusdAmm]
+ [contracts.bLusdAmm, isMainnet, liquity.connection.signer, account]
);
const getExpectedLpTokens = useCallback(
async (bLusdAmount: Decimal, lusdAmount: Decimal) => {
- if (liquity.connection.signer === undefined) return Decimal.ZERO;
-
return contracts.bLusdAmmZapper
? api.getExpectedLpTokens(bLusdAmount, lusdAmount, contracts.bLusdAmmZapper)
: Decimal.ZERO;
},
- [contracts.bLusdAmmZapper, liquity.connection.signer]
+ [contracts.bLusdAmmZapper]
);
const [manageLiquidity, manageLiquidityStatus] = useTransaction(
@@ -472,14 +525,17 @@ export const BondViewProvider: React.FC = props => {
params.lusdAmount,
params.minLpTokens,
params.shouldStakeInGauge,
- contracts.bLusdAmmZapper
+ contracts.bLusdAmmZapper,
+ liquity.connection.signer,
+ account
);
} else if (params.action === "removeLiquidity") {
await api.removeLiquidity(
params.burnLpTokens,
params.minBLusdAmount,
params.minLusdAmount,
- contracts.bLusdAmmZapper
+ contracts.bLusdAmmZapper,
+ liquity.connection.signer
);
} else if (params.action === "removeLiquidityOneCoin") {
await api.removeLiquidityOneCoin(
@@ -487,18 +543,34 @@ export const BondViewProvider: React.FC = props => {
params.output,
params.minAmount,
contracts.bLusdAmmZapper,
- contracts.bLusdAmm
+ contracts.bLusdAmm,
+ liquity.connection.signer,
+ account
);
} else if (params.action === "stakeLiquidity") {
- await api.stakeLiquidity(params.stakeAmount, contracts.bLusdGauge);
+ await api.stakeLiquidity(
+ params.stakeAmount,
+ contracts.bLusdGauge,
+ liquity.connection.signer
+ );
} else if (params.action === "unstakeLiquidity") {
- await api.unstakeLiquidity(params.unstakeAmount, contracts.bLusdGauge);
+ await api.unstakeLiquidity(
+ params.unstakeAmount,
+ contracts.bLusdGauge,
+ liquity.connection.signer
+ );
} else if (params.action === "claimLpRewards") {
- await api.claimLpRewards(contracts.bLusdGauge);
+ await api.claimLpRewards(contracts.bLusdGauge, liquity.connection.signer);
}
setShouldSynchronize(true);
},
- [contracts.bLusdAmmZapper, contracts.bLusdGauge]
+ [
+ contracts.bLusdAmmZapper,
+ contracts.bLusdGauge,
+ contracts.bLusdAmm,
+ liquity.connection.signer,
+ account
+ ]
);
const getExpectedWithdrawal = useCallback(
@@ -546,7 +618,9 @@ export const BondViewProvider: React.FC = props => {
viewRef.current === _view && event === _event;
try {
- if (isCurrentViewEvent("CREATING", "CONFIRM_PRESSED")) {
+ if (isCurrentViewEvent("CREATING", "APPROVE_PRESSED")) {
+ await approveInfiniteBond();
+ } else if (isCurrentViewEvent("CREATING", "CONFIRM_PRESSED")) {
await createBond((payload as CreateBondPayload).deposit);
await dispatchEvent("CREATE_BOND_CONFIRMED");
} else if (isCurrentViewEvent("CANCELLING", "CONFIRM_PRESSED")) {
@@ -585,6 +659,7 @@ export const BondViewProvider: React.FC = props => {
},
[
selectedBondId,
+ approveInfiniteBond,
cancelBond,
createBond,
claimBond,
@@ -600,6 +675,7 @@ export const BondViewProvider: React.FC = props => {
useEffect(() => {
setStatuses(statuses => ({
...statuses,
+ APPROVE: approveStatus,
CREATE: createStatus,
CANCEL: cancelStatus,
CLAIM: claimStatus,
@@ -609,6 +685,7 @@ export const BondViewProvider: React.FC = props => {
MANAGE_LIQUIDITY: manageLiquidityStatus
}));
}, [
+ approveStatus,
createStatus,
cancelStatus,
claimStatus,
@@ -663,6 +740,7 @@ export const BondViewProvider: React.FC = props => {
lpTokenSupply,
bLusdAmmBLusdBalance,
bLusdAmmLusdBalance,
+ isInfiniteBondApproved,
isSynchronizing,
getLusdFromFaucet,
setSimulatedMarketPrice,
@@ -689,8 +767,7 @@ export const BondViewProvider: React.FC = props => {
lpRewards
};
- // @ts-ignore
- window.__LIQUITY_BONDS__ = provider.current;
+ // window.__LIQUITY_BONDS__ = provider.current;
return {children};
};
diff --git a/packages/dev-frontend/src/components/Bonds/context/api.ts b/packages/dev-frontend/src/components/Bonds/context/api.ts
index dc6c0f9a3..8c2d3e486 100644
--- a/packages/dev-frontend/src/components/Bonds/context/api.ts
+++ b/packages/dev-frontend/src/components/Bonds/context/api.ts
@@ -8,7 +8,7 @@ import {
providers,
Signer
} from "ethers";
-import { splitSignature } from "ethers/lib/utils";
+// import { splitSignature } from "ethers/lib/utils";
import type {
BLUSDToken,
BondNFT,
@@ -52,7 +52,6 @@ import {
TokenExchangeEvent,
TokenExchangeEventObject
} from "@liquity/chicken-bonds/lusd/types/external/CurveCryptoSwap2ETH";
-import type { EthersSigner } from "@liquity/lib-ethers";
import mainnet from "@liquity/chicken-bonds/lusd/addresses/mainnet.json";
import type {
CurveLiquidityGaugeV5,
@@ -137,7 +136,7 @@ type CachedYearnApys = {
bLusdLusd3Crv: Decimal | undefined;
};
-let cachedApys: CachedYearnApys = {
+const cachedApys: CachedYearnApys = {
lusd3Crv: undefined,
stabilityPool: undefined,
bLusdLusd3Crv: undefined
@@ -535,7 +534,7 @@ const getProtocolInfo = async (
let yieldAmplification: Maybe = undefined;
let bLusdApr: Maybe = undefined;
- let bLusdLpApr: Maybe = cachedApys.bLusdLusd3Crv;
+ const bLusdLpApr: Maybe = cachedApys.bLusdLusd3Crv;
const fairPrice = {
lower: treasury.total.sub(treasury.pending).div(bLusdSupply),
@@ -655,7 +654,84 @@ const getTokenTotalSupply = async (token: ERC20): Promise => {
return decimalify(await token.totalSupply());
};
+const isInfiniteBondApproved = async (
+ account: string,
+ lusdToken: LUSDToken,
+ chickenBondManager: ChickenBondManager
+): Promise => {
+ const allowance = await lusdToken.allowance(account, chickenBondManager.address);
+
+ // Unlike bLUSD, LUSD doesn't explicitly handle infinite approvals, therefore the allowance will
+ // start to decrease from 2**64.
+ // However, it is practically impossible that it would decrease below 2**63.
+ return allowance.gt(constants.MaxInt256);
+};
+
+const approveInfiniteBond = async (
+ lusdToken: LUSDToken | undefined,
+ chickenBondManager: ChickenBondManager | undefined,
+ signer: Signer | undefined
+): Promise => {
+ if (lusdToken === undefined || chickenBondManager === undefined || signer === undefined) {
+ throw new Error("approveInfiniteBond() failed: a dependency is null");
+ }
+
+ console.log("approveInfiniteBond() started");
+
+ try {
+ await (
+ await ((lusdToken as unknown) as Contract)
+ .connect(signer)
+ .approve(chickenBondManager.address, constants.MaxUint256._hex)
+ ).wait();
+
+ console.log("approveInfiniteBond() succceeded");
+ } catch (error: unknown) {
+ throw new Error(`approveInfiniteBond() failed: ${error}`);
+ }
+};
+
const createBond = async (
+ lusdAmount: Decimal,
+ owner: string,
+ chickenBondManager: ChickenBondManager | undefined,
+ signer: Signer | undefined
+): Promise => {
+ if (chickenBondManager === undefined || signer === undefined) {
+ throw new Error("createBond() failed: a dependency is null");
+ }
+
+ const gasEstimate = await chickenBondManager.estimateGas.createBond(lusdAmount.hex, {
+ from: owner
+ });
+
+ const receipt = await (
+ await chickenBondManager.connect(signer).createBond(lusdAmount.hex, {
+ gasLimit: gasEstimate.add(LQTY_ISSUANCE_GAS_HEADROOM)
+ })
+ ).wait();
+
+ console.log(
+ "CREATE BOND",
+ receipt?.events,
+ receipt?.events?.map(c => c.event),
+ receipt?.events?.find(e => e.event === "BondCreated")
+ );
+
+ const createdEvent = receipt?.events?.find(
+ e => e.event === "BondCreated"
+ ) as Maybe;
+
+ if (createdEvent === undefined) {
+ throw new Error("createBond() failed: couldn't find BondCreated event");
+ }
+
+ console.log("createBond() finished:", createdEvent.args);
+ return createdEvent.args;
+};
+
+/*
+const createBondWithPermit = async (
lusdAmount: Decimal,
owner: string,
lusdAddress: string,
@@ -664,7 +740,7 @@ const createBond = async (
signer: EthersSigner
): Promise => {
if (chickenBondManager === undefined || lusdToken === undefined) {
- throw new Error("createBond() failed: a dependency is null");
+ throw new Error("createBondWithPermit() failed: a dependency is null");
}
const TEN_MINUTES_IN_SECONDS = 60 * 10;
@@ -731,19 +807,27 @@ const createBond = async (
console.log("createBond() finished:", createdEvent.args);
return createdEvent.args;
};
+*/
const cancelBond = async (
bondId: string,
minimumLusd: Decimal,
- chickenBondManager: ChickenBondManager | undefined
+ owner: string,
+ chickenBondManager: ChickenBondManager | undefined,
+ signer: Signer | undefined
): Promise => {
- if (chickenBondManager === undefined) throw new Error("cancelBond() failed: a dependency is null");
+ if (chickenBondManager === undefined || signer === undefined) {
+ throw new Error("cancelBond() failed: a dependency is null");
+ }
+
console.log("cancelBond() started:", bondId, minimumLusd.toString());
- const gasEstimate = await chickenBondManager.estimateGas.chickenOut(bondId, minimumLusd.hex);
+ const gasEstimate = await chickenBondManager.estimateGas.chickenOut(bondId, minimumLusd.hex, {
+ from: owner
+ });
const receipt = await (
- await chickenBondManager.chickenOut(bondId, minimumLusd.hex, {
+ await chickenBondManager.connect(signer).chickenOut(bondId, minimumLusd.hex, {
gasLimit: gasEstimate.add(LQTY_ISSUANCE_GAS_HEADROOM)
})
).wait();
@@ -755,23 +839,28 @@ const cancelBond = async (
if (cancelledEvent === undefined) {
throw new Error("cancelBond() failed: couldn't find BondCancelled event");
}
+
console.log("cancelBond() finished:", cancelledEvent.args);
return cancelledEvent.args;
};
const claimBond = async (
bondId: string,
- chickenBondManager: ChickenBondManager | undefined
+ owner: string,
+ chickenBondManager: ChickenBondManager | undefined,
+ signer: Signer | undefined
): Promise => {
try {
- if (chickenBondManager === undefined)
+ if (chickenBondManager === undefined || signer === undefined) {
throw new Error("claimBond() failed: a dependency is null");
+ }
+
console.log("claimBond() started", bondId);
- const gasEstimate = await chickenBondManager.estimateGas.chickenIn(bondId);
+ const gasEstimate = await chickenBondManager.estimateGas.chickenIn(bondId, { from: owner });
const receipt = await (
- await chickenBondManager.chickenIn(bondId, {
+ await chickenBondManager.connect(signer).chickenIn(bondId, {
gasLimit: gasEstimate.add(LQTY_ISSUANCE_GAS_HEADROOM)
})
).wait();
@@ -835,34 +924,47 @@ const isTokenApprovedWithAmmZapper = async (
const approveTokenWithBLusdAmm = async (
token: LUSDToken | BLUSDToken | undefined,
- bLusdAmmAddress: string | null
+ bLusdAmmAddress: string | null,
+ signer: Signer | undefined
) => {
- if (token === undefined || bLusdAmmAddress === null) {
+ if (token === undefined || bLusdAmmAddress === null || signer === undefined) {
throw new Error("approveTokenWithBLusdAmm() failed: a dependency is null");
}
- await (await token.approve(bLusdAmmAddress, constants.MaxUint256)).wait();
+ await (
+ await (token as Contract).connect(signer).approve(bLusdAmmAddress, constants.MaxUint256)
+ ).wait();
return;
};
const approveToken = async (
token: LUSDToken | BLUSDToken | ERC20 | undefined,
- spenderAddress: string | null
+ spenderAddress: string | null,
+ signer: Signer | undefined
) => {
- if (token === undefined || spenderAddress === null) {
+ if (token === undefined || spenderAddress === null || signer === undefined) {
throw new Error("approveToken() failed: a dependency is null");
}
- await (await token.approve(spenderAddress, constants.MaxUint256)).wait();
+ await (
+ await (token as Contract).connect(signer).approve(spenderAddress, constants.MaxUint256)
+ ).wait();
return;
};
-const approveTokenWithBLusdAmmMainnet = async (token: LUSDToken | BLUSDToken | undefined) => {
- if (token === undefined) {
+const approveTokenWithBLusdAmmMainnet = async (
+ token: LUSDToken | BLUSDToken | undefined,
+ signer: Signer | undefined
+) => {
+ if (token === undefined || signer === undefined) {
throw new Error("approveTokenWithBLusdAmmMainnet() failed: a dependency is null");
}
- await (await token.approve(CURVE_REGISTRY_SWAPS_ADDRESS, constants.MaxUint256)).wait();
+ await (
+ await (token as Contract)
+ .connect(signer)
+ .approve(CURVE_REGISTRY_SWAPS_ADDRESS, constants.MaxUint256)
+ ).wait();
return;
};
@@ -886,7 +988,7 @@ const getExpectedSwapOutputMainnet = async (
const reciprocal = Decimal.from(1).div(1.29);
if (bLusdAmmBalance.eq(0)) return inputAmount.div(reciprocal);
- const swaps = CurveRegistrySwaps__factory.connect(CURVE_REGISTRY_SWAPS_ADDRESS, bLusdAmm.signer);
+ const swaps = CurveRegistrySwaps__factory.connect(CURVE_REGISTRY_SWAPS_ADDRESS, bLusdAmm.provider);
return decimalify(
await swaps["get_exchange_multiple_amount(address[9],uint256[3][4],uint256)"](
@@ -900,19 +1002,20 @@ const swapTokens = async (
inputToken: BLusdAmmTokenIndex,
inputAmount: Decimal,
minOutputAmount: Decimal,
- bLusdAmm: CurveCryptoSwap2ETH | undefined
+ bLusdAmm: CurveCryptoSwap2ETH | undefined,
+ signer: Signer | undefined,
+ account: string
): Promise => {
- if (bLusdAmm === undefined) throw new Error("swapTokens() failed: a dependency is null");
+ if (bLusdAmm === undefined || signer === undefined) {
+ throw new Error("swapTokens() failed: a dependency is null");
+ }
- const gasEstimate = await bLusdAmm.estimateGas["exchange(uint256,uint256,uint256,uint256)"](
- inputToken,
- getOtherToken(inputToken),
- inputAmount.hex,
- minOutputAmount.hex
- );
+ const gasEstimate = await bLusdAmm.estimateGas[
+ "exchange(uint256,uint256,uint256,uint256)"
+ ](inputToken, getOtherToken(inputToken), inputAmount.hex, minOutputAmount.hex, { from: account });
const receipt = await (
- await bLusdAmm["exchange(uint256,uint256,uint256,uint256)"](
+ await bLusdAmm.connect(signer)["exchange(uint256,uint256,uint256,uint256)"](
inputToken,
getOtherToken(inputToken),
inputAmount.hex,
@@ -937,19 +1040,23 @@ const swapTokensMainnet = async (
inputToken: BLusdAmmTokenIndex,
inputAmount: Decimal,
minOutputAmount: Decimal,
- bLusdAmm: CurveCryptoSwap2ETH | undefined
+ bLusdAmm: CurveCryptoSwap2ETH | undefined,
+ signer: Signer | undefined,
+ account: string
): Promise => {
- if (bLusdAmm === undefined) throw new Error("swapTokensMainnet() failed: a dependency is null");
+ if (bLusdAmm === undefined || signer === undefined) {
+ throw new Error("swapTokensMainnet() failed: a dependency is null");
+ }
const swaps = CurveRegistrySwaps__factory.connect(CURVE_REGISTRY_SWAPS_ADDRESS, bLusdAmm.signer);
const route = getRoute(inputToken);
const gasEstimate = await swaps.estimateGas[
"exchange_multiple(address[9],uint256[3][4],uint256,uint256)"
- ](...route, inputAmount.hex, minOutputAmount.hex);
+ ](...route, inputAmount.hex, minOutputAmount.hex, { from: account });
const receipt = await (
- await swaps["exchange_multiple(address[9],uint256[3][4],uint256,uint256)"](
+ await swaps.connect(signer)["exchange_multiple(address[9],uint256[3][4],uint256,uint256)"](
...route,
inputAmount.hex,
minOutputAmount.hex,
@@ -999,20 +1106,25 @@ const addLiquidity = async (
lusdAmount: Decimal,
minLpTokens: Decimal,
shouldStakeInGauge: boolean,
- bLusdZapper: BLUSDLPZap | undefined
+ bLusdZapper: BLUSDLPZap | undefined,
+ signer: Signer | undefined,
+ account: string
): Promise => {
- if (bLusdZapper === undefined) throw new Error("addLiquidity() failed: a dependency is null");
+ if (bLusdZapper === undefined || signer === undefined) {
+ throw new Error("addLiquidity() failed: a dependency is null");
+ }
const zapperFunction = shouldStakeInGauge ? "addLiquidityAndStake" : "addLiquidity";
const gasEstimate = await bLusdZapper.estimateGas[zapperFunction](
bLusdAmount.hex,
lusdAmount.hex,
- minLpTokens.hex
+ minLpTokens.hex,
+ { from: account }
);
const receipt = await (
- await bLusdZapper[zapperFunction](
+ await bLusdZapper.connect(signer)[zapperFunction](
bLusdAmount.hex,
lusdAmount.hex,
minLpTokens.hex,
@@ -1056,16 +1168,17 @@ const removeLiquidity = async (
burnLpTokens: Decimal,
minBLusdAmount: Decimal,
minLusdAmount: Decimal,
- bLusdZapper: BLUSDLPZap | undefined
+ bLusdZapper: BLUSDLPZap | undefined,
+ signer: Signer | undefined
): Promise => {
- if (bLusdZapper === undefined) throw new Error("removeLiquidity() failed: a dependency is null");
+ if (bLusdZapper === undefined || signer === undefined) {
+ throw new Error("removeLiquidity() failed: a dependency is null");
+ }
const receipt = await (
- await bLusdZapper.removeLiquidityBalanced(
- burnLpTokens.hex,
- minBLusdAmount.hex,
- minLusdAmount.hex
- )
+ await bLusdZapper
+ .connect(signer)
+ .removeLiquidityBalanced(burnLpTokens.hex, minBLusdAmount.hex, minLusdAmount.hex)
).wait();
if (!receipt.status) {
@@ -1078,20 +1191,24 @@ const removeLiquidity = async (
const removeLiquidityLUSD = async (
burnLpTokens: Decimal,
minAmount: Decimal,
- bLusdZapper: BLUSDLPZap | undefined
+ bLusdZapper: BLUSDLPZap | undefined,
+ signer: Signer | undefined,
+ account: string
): Promise => {
- if (bLusdZapper === undefined)
+ if (bLusdZapper === undefined || signer === undefined) {
throw new Error("removeLiquidityLUSD() failed: a dependency is null");
+ }
const removeLiquidityFunction = "removeLiquidityLUSD";
const gasEstimate = await bLusdZapper.estimateGas[removeLiquidityFunction](
burnLpTokens.hex,
- minAmount.hex
+ minAmount.hex,
+ { from: account }
);
const receipt = await (
- await bLusdZapper[removeLiquidityFunction](
+ await bLusdZapper.connect(signer)[removeLiquidityFunction](
burnLpTokens.hex,
minAmount.hex,
{ gasLimit: gasEstimate.mul(6).div(5) } // Add 20% overhead (we've seen it fail otherwise)
@@ -1108,9 +1225,13 @@ const removeLiquidityLUSD = async (
const removeLiquidityBLUSD = async (
burnLpTokens: Decimal,
minAmount: Decimal,
- bLusdAmm: CurveCryptoSwap2ETH | undefined
+ bLusdAmm: CurveCryptoSwap2ETH | undefined,
+ signer: Signer | undefined,
+ account: string
): Promise => {
- if (bLusdAmm === undefined) throw new Error("removeLiquidityBLUSD() failed: a dependency is null");
+ if (bLusdAmm === undefined || signer === undefined) {
+ throw new Error("removeLiquidityBLUSD() failed: a dependency is null");
+ }
const removeLiquidityFunction = "remove_liquidity_one_coin(uint256,uint256,uint256,bool)";
@@ -1118,11 +1239,12 @@ const removeLiquidityBLUSD = async (
burnLpTokens.hex,
0,
minAmount.hex,
- false
+ false,
+ { from: account }
);
const receipt = await (
- await bLusdAmm[removeLiquidityFunction](
+ await bLusdAmm.connect(signer)[removeLiquidityFunction](
burnLpTokens.hex,
0,
minAmount.hex,
@@ -1143,23 +1265,29 @@ const removeLiquidityOneCoin = async (
output: BLusdAmmTokenIndex,
minAmount: Decimal,
bLusdZapper: BLUSDLPZap | undefined,
- bLusdAmm: CurveCryptoSwap2ETH | undefined
+ bLusdAmm: CurveCryptoSwap2ETH | undefined,
+ signer: Signer | undefined,
+ account: string
): Promise => {
if (output === BLusdAmmTokenIndex.LUSD) {
- return removeLiquidityLUSD(burnLpTokens, minAmount, bLusdZapper);
+ return removeLiquidityLUSD(burnLpTokens, minAmount, bLusdZapper, signer, account);
} else {
- return removeLiquidityBLUSD(burnLpTokens, minAmount, bLusdAmm);
+ return removeLiquidityBLUSD(burnLpTokens, minAmount, bLusdAmm, signer, account);
}
};
const stakeLiquidity = async (
stakeAmount: Decimal,
- bLusdGauge: CurveLiquidityGaugeV5 | undefined
+ bLusdGauge: CurveLiquidityGaugeV5 | undefined,
+ signer: Signer | undefined
): Promise => {
- if (bLusdGauge === undefined) {
+ if (bLusdGauge === undefined || signer === undefined) {
throw new Error("stakeLiquidity() failed: a dependency is null");
}
- const receipt = await (await bLusdGauge["deposit(uint256)"](stakeAmount.hex)).wait();
+
+ const receipt = await (
+ await bLusdGauge.connect(signer)["deposit(uint256)"](stakeAmount.hex)
+ ).wait();
const depositEvent = receipt?.events?.find(e => e?.event === "Deposit") as Maybe;
@@ -1173,14 +1301,15 @@ const stakeLiquidity = async (
const unstakeLiquidity = async (
unstakeAmount: Decimal,
- bLusdGauge: CurveLiquidityGaugeV5 | undefined
+ bLusdGauge: CurveLiquidityGaugeV5 | undefined,
+ signer: Signer | undefined
): Promise => {
- if (bLusdGauge === undefined) {
+ if (bLusdGauge === undefined || signer === undefined) {
throw new Error("unstakeLiquidity() failed: a dependency is null");
}
- const claimRewards = true;
+
const receipt = await (
- await bLusdGauge["withdraw(uint256,bool)"](unstakeAmount.hex, claimRewards)
+ await bLusdGauge.connect(signer)["withdraw(uint256,bool)"](unstakeAmount.hex, true)
).wait();
const withdrawEvent = receipt?.events?.find(e => e?.event === "Withdraw") as Maybe;
@@ -1193,12 +1322,15 @@ const unstakeLiquidity = async (
return withdrawEvent.args;
};
-const claimLpRewards = async (bLusdGauge: CurveLiquidityGaugeV5 | undefined): Promise => {
- if (bLusdGauge === undefined) {
+const claimLpRewards = async (
+ bLusdGauge: CurveLiquidityGaugeV5 | undefined,
+ signer: Signer | undefined
+): Promise => {
+ if (bLusdGauge === undefined || signer === undefined) {
throw new Error("claimLpRewards() failed: a dependency is null");
}
- const receipt = await (await bLusdGauge["claim_rewards()"]()).wait();
+ const receipt = await (await bLusdGauge.connect(signer)["claim_rewards()"]()).wait();
if (!receipt.status) {
throw new Error("claimLpRewards() failed: no transaction receipt status received.");
@@ -1209,7 +1341,7 @@ const getLpRewards = async (
account: string,
bLusdGauge: CurveLiquidityGaugeV5
): Promise => {
- let rewards: BLusdLpRewards = [];
+ const rewards: BLusdLpRewards = [];
const totalRewardTokens = (await bLusdGauge.reward_count()).toNumber();
@@ -1233,6 +1365,8 @@ export const api = {
getTokenBalance,
getTokenTotalSupply,
getProtocolInfo,
+ approveInfiniteBond,
+ isInfiniteBondApproved,
createBond,
cancelBond,
claimBond,
diff --git a/packages/dev-frontend/src/components/Bonds/context/transitions.ts b/packages/dev-frontend/src/components/Bonds/context/transitions.ts
index cfdbe2448..ebc332f25 100644
--- a/packages/dev-frontend/src/components/Bonds/context/transitions.ts
+++ b/packages/dev-frontend/src/components/Bonds/context/transitions.ts
@@ -59,6 +59,7 @@ export const transitions: BondEventTransitions = {
ABORT_PRESSED: "IDLE",
CREATE_BOND_CONFIRMED: "IDLE",
BACK_PRESSED: "IDLE",
+ APPROVE_PRESSED: "CREATING",
CONFIRM_PRESSED: "CREATING"
},
CLAIMING: {
@@ -245,6 +246,7 @@ export type ProtocolInfo = {
export type TransactionStatus = "IDLE" | "PENDING" | "CONFIRMED" | "FAILED";
export type BondTransaction =
+ | "APPROVE"
| "CREATE"
| "CANCEL"
| "CLAIM"
diff --git a/packages/dev-frontend/src/components/Bonds/context/useBondContracts.tsx b/packages/dev-frontend/src/components/Bonds/context/useBondContracts.tsx
index 905247c30..1e6a41a2a 100644
--- a/packages/dev-frontend/src/components/Bonds/context/useBondContracts.tsx
+++ b/packages/dev-frontend/src/components/Bonds/context/useBondContracts.tsx
@@ -106,7 +106,7 @@ export const useBondContracts = (): BondContracts => {
CurveCryptoSwap2ETH__factory.abi
);
- const [bLusdAmmZapper, bLusAmmZapperStatus] = useContract(
+ const [bLusdAmmZapper, bLusdAmmZapperStatus] = useContract(
BLUSD_LP_ZAP_ADDRESS,
BLUSDLPZap__factory.abi
);
@@ -123,7 +123,7 @@ export const useBondContracts = (): BondContracts => {
chickenBondManagerStatus,
bLusdTokenStatus,
bLusdAmmStatus,
- bLusAmmZapperStatus,
+ ...(isMainnet ? [bLusdAmmZapperStatus] : []),
bLusdGaugeStatus
].find(status => status === "FAILED") === undefined;
@@ -135,8 +135,7 @@ export const useBondContracts = (): BondContracts => {
chickenBondManager === undefined ||
bLusdToken === undefined ||
bLusdAmm === undefined ||
- bLusdGauge === undefined ||
- BLUSD_AMM_STAKING_ADDRESS === null
+ bLusdGauge === undefined
) {
return undefined;
}
@@ -155,11 +154,10 @@ export const useBondContracts = (): BondContracts => {
await protocolInfoPromise
);
- const [protocolInfo, stats, lpToken, lpStakingContract] = await Promise.all([
+ const [protocolInfo, stats, lpToken] = await Promise.all([
protocolInfoPromise,
api.getStats(chickenBondManager),
- api.getLpToken(bLusdAmm),
- api.erc20From(BLUSD_AMM_STAKING_ADDRESS, bLusdAmm.provider)
+ api.getLpToken(bLusdAmm)
]);
const [
@@ -174,10 +172,10 @@ export const useBondContracts = (): BondContracts => {
api.getTokenBalance(account, bLusdToken),
api.getTokenBalance(account, lusdToken),
api.getTokenBalance(account, lpToken),
- api.getTokenBalance(account, lpStakingContract),
+ isMainnet ? api.getTokenBalance(account, bLusdGauge) : Decimal.ZERO,
api.getTokenTotalSupply(lpToken),
api.getCoinBalances(bLusdAmm),
- api.getLpRewards(account, bLusdGauge)
+ isMainnet ? api.getLpRewards(account, bLusdGauge) : []
]);
const bonds = await bondsPromise;
@@ -196,16 +194,7 @@ export const useBondContracts = (): BondContracts => {
lpRewards
};
},
- [
- chickenBondManager,
- bondNft,
- bLusdToken,
- lusdToken,
- bLusdAmm,
- isMainnet,
- BLUSD_AMM_STAKING_ADDRESS,
- bLusdGauge
- ]
+ [chickenBondManager, bondNft, bLusdToken, lusdToken, bLusdAmm, isMainnet, bLusdGauge]
);
return {
diff --git a/packages/dev-frontend/src/components/Bonds/views/creating/Details.tsx b/packages/dev-frontend/src/components/Bonds/views/creating/Details.tsx
index d34430403..52d39ebe4 100644
--- a/packages/dev-frontend/src/components/Bonds/views/creating/Details.tsx
+++ b/packages/dev-frontend/src/components/Bonds/views/creating/Details.tsx
@@ -1,12 +1,13 @@
/** @jsxImportSource theme-ui */
import React, { useEffect, useMemo, useState } from "react";
-import { Flex, Heading, Button, Card, Grid, Close, Image, Spinner } from "theme-ui";
+import { Flex, Heading, Button, Card, Grid, Close, Text, Image, Spinner } from "theme-ui";
import { Decimal } from "@liquity/lib-base";
import { EditableRow } from "../../../Trove/Editor";
import { Record } from "../../Record";
import { InfoIcon } from "../../../InfoIcon";
import { useBondView } from "../../context/BondViewContext";
import { HorizontalTimeline, Label, SubLabel, UNKNOWN_DATE } from "../../../HorizontalTimeline";
+import { ActionDescription } from "../../../ActionDescription";
import { EXAMPLE_NFT } from "../../context/BondViewProvider";
import * as l from "../../lexicon";
import { useWizard } from "../../../Wizard/Context";
@@ -34,6 +35,7 @@ export const Details: React.FC = ({ onBack }) => {
const {
dispatchEvent,
statuses,
+ isInfiniteBondApproved,
lusdBalance,
simulatedProtocolInfo,
setSimulatedMarketPrice,
@@ -43,7 +45,10 @@ export const Details: React.FC = ({ onBack }) => {
const { back } = useWizard();
const [deposit, setDeposit] = useState(lusdBalance ?? Decimal.ZERO);
const depositEditingState = useState();
- const isConfirming = useMemo(() => statuses.CREATE === "PENDING", [statuses.CREATE]);
+ const isApprovingOrConfirming = useMemo(
+ () => statuses.APPROVE === "PENDING" || statuses.CREATE === "PENDING",
+ [statuses.APPROVE, statuses.CREATE]
+ );
const handleBack = back ?? onBack ?? (() => dispatchEvent("BACK_PRESSED"));
const [isDepositEnough, setIsDepositEnough] = useState(lusdBalance?.gte(100) ?? true);
const [doesDepositExceedBalance, setDoesDepositExceedBalance] = useState(false);
@@ -52,6 +57,10 @@ export const Details: React.FC = ({ onBack }) => {
dispatchEvent("ABORT_PRESSED");
};
+ const handleApprovePressed = () => {
+ dispatchEvent("APPROVE_PRESSED");
+ };
+
const handleConfirmPressed = () => {
dispatchEvent("CONFIRM_PRESSED", { deposit } as CreateBondPayload);
};
@@ -259,8 +268,6 @@ export const Details: React.FC = ({ onBack }) => {
onReset={() => resetSimulatedMarketPrice()}
/>
- {statuses.CREATE === "FAILED" && Failed to create bond. Please try again.}
-
{!protocolInfo.hasMarketPremium && (
When the bLUSD market price is less than 3% above the floor price, it's not profitable to
@@ -269,6 +276,24 @@ export const Details: React.FC = ({ onBack }) => {
)}
+ {!isInfiniteBondApproved && (
+
+ You are approving LUSD for bonding
+
+ )}
+
+ {statuses.APPROVE === "FAILED" && (
+ Failed to approve spend of LUSD. Please try again.
+ )}
+
+ {statuses.CREATE === "FAILED" && Failed to create bond. Please try again.}
+
+ {isInfiniteBondApproved && (
+
+ You are bonding {deposit.prettify(2)} LUSD
+
+ )}
+
{!isDepositEnough && The minimum bond amount is 100 LUSD.}
{doesDepositExceedBalance && (
@@ -281,13 +306,23 @@ export const Details: React.FC = ({ onBack }) => {
-
>
);
diff --git a/packages/dev-frontend/src/components/UserAccount.tsx b/packages/dev-frontend/src/components/UserAccount.tsx
index 55ada904f..7c2f6b648 100644
--- a/packages/dev-frontend/src/components/UserAccount.tsx
+++ b/packages/dev-frontend/src/components/UserAccount.tsx
@@ -1,5 +1,5 @@
import React from "react";
-import { Text, Flex, Box, Heading } from "theme-ui";
+import { Text, Flex, Box, Heading, Button } from "theme-ui";
import { Decimal, LiquityStoreState } from "@liquity/lib-base";
import { useLiquitySelector } from "@liquity/lib-react";
@@ -11,6 +11,7 @@ import { shortenAddress } from "../utils/shortenAddress";
import { Icon } from "./Icon";
import { useBondView } from "./Bonds/context/BondViewContext";
import { useBondAddresses } from "./Bonds/context/BondAddressesContext";
+import { ConnectKitButton } from "connectkit";
const select = ({ accountBalance, lusdBalance, lqtyBalance }: LiquityStoreState) => ({
accountBalance,
@@ -27,18 +28,28 @@ export const UserAccount: React.FC = () => {
const lusdBalance = LUSD_OVERRIDE_ADDRESS === null ? realLusdBalance : customLusdBalance;
return (
-
-
-
-
- Connected as
-
- {shortenAddress(account)}
-
-
-
-
-
+
+
+ {connectKit => (
+
+
+
+ {shortenAddress(account)}
+
+
+ )}
+
+
+
{([
@@ -52,7 +63,7 @@ export const UserAccount: React.FC = () => {
{balance.prettify()}
))}
-
-
+
+
);
};
diff --git a/packages/dev-frontend/src/components/WalletConnector.tsx b/packages/dev-frontend/src/components/WalletConnector.tsx
index 89c1a07f4..183149107 100644
--- a/packages/dev-frontend/src/components/WalletConnector.tsx
+++ b/packages/dev-frontend/src/components/WalletConnector.tsx
@@ -1,9 +1,26 @@
import { ConnectKitButton } from "connectkit";
+import { Box, Button, Flex } from "theme-ui";
+import { Icon } from "./Icon";
type WalletConnectorProps = {
loader?: React.ReactNode;
};
-export const WalletConnector: React.FC = () => {
- return ;
+export const WalletConnector: React.FC = ({ children }) => {
+ return (
+
+ {connectKit =>
+ connectKit.isConnected ? (
+ children
+ ) : (
+
+
+
+ Connect wallet
+
+
+ )
+ }
+
+ );
};
diff --git a/packages/dev-frontend/src/config/index.ts b/packages/dev-frontend/src/config/index.ts
index e8e4e6893..419f98069 100644
--- a/packages/dev-frontend/src/config/index.ts
+++ b/packages/dev-frontend/src/config/index.ts
@@ -4,11 +4,14 @@ import { isAddress, getAddress } from "@ethersproject/address";
export type LiquityFrontendConfig = {
frontendTag: string;
infuraApiKey?: string;
+ alchemyApiKey?: string;
testnetOnly?: boolean;
+ walletConnectProjectId: string;
};
const defaultConfig: LiquityFrontendConfig = {
- frontendTag: AddressZero
+ frontendTag: AddressZero,
+ walletConnectProjectId: "b16efb4fd41473c0f45dbad8efa15a00"
};
function hasKey(o: object, k: K): o is Record {
diff --git a/packages/dev-frontend/src/hooks/LiquityContext.tsx b/packages/dev-frontend/src/hooks/LiquityContext.tsx
index 5de9b3423..a16e58a1c 100644
--- a/packages/dev-frontend/src/hooks/LiquityContext.tsx
+++ b/packages/dev-frontend/src/hooks/LiquityContext.tsx
@@ -24,7 +24,7 @@ const LiquityContext = createContext(undefined)
type LiquityProviderProps = {
loader?: React.ReactNode;
- unsupportedNetworkFallback?: (chainId: number) => React.ReactNode;
+ unsupportedNetworkFallback?: React.ReactNode;
unsupportedMainnetFallback?: React.ReactNode;
};
@@ -55,7 +55,9 @@ export const LiquityProvider: React.FC = ({
frontendTag: config.frontendTag,
useStore: "blockPolled"
});
- } catch {}
+ } catch (err) {
+ console.error(err);
+ }
}
}, [config, provider, signer.data, account.address, chainId]);
@@ -91,7 +93,7 @@ export const LiquityProvider: React.FC = ({
}
}, [config, connection]);
- if (!config || !provider || !account.address || !chainId) {
+ if (!config || !account.address || !connection) {
return <>{loader}>;
}
@@ -100,14 +102,16 @@ export const LiquityProvider: React.FC = ({
}
if (!connection) {
- return unsupportedNetworkFallback ? <>{unsupportedNetworkFallback(chainId)}> : null;
+ return <>{unsupportedNetworkFallback}>;
}
const liquity = EthersLiquity._from(connection);
liquity.store.logging = true;
return (
-
+
{children}
);
diff --git a/packages/dev-frontend/src/hooks/useContract.ts b/packages/dev-frontend/src/hooks/useContract.ts
index d55174858..035499be3 100644
--- a/packages/dev-frontend/src/hooks/useContract.ts
+++ b/packages/dev-frontend/src/hooks/useContract.ts
@@ -9,7 +9,7 @@ export function useContract(
address: string | null,
abi: ContractInterface
): [TContractType | undefined, ContractStatus] {
- const { provider, liquity } = useLiquity();
+ const { provider } = useLiquity();
const [contract, setContract] = useState>();
useEffect(() => {
@@ -27,7 +27,7 @@ export function useContract(
const connectedContract = (new ethers.Contract(
address,
abi,
- liquity.connection.signer
+ provider
) as unknown) as TContractType;
setContract({ instance: connectedContract, status: "LOADED" });
@@ -39,7 +39,7 @@ export function useContract(
);
}
})();
- }, [provider, liquity.connection.signer, address, abi, contract]);
+ }, [provider, address, abi, contract]);
return [contract?.instance, contract?.status || "UNKNOWN"];
}
diff --git a/packages/dev-frontend/vite.config.ts b/packages/dev-frontend/vite.config.ts
index a249c9209..5c9d6efae 100644
--- a/packages/dev-frontend/vite.config.ts
+++ b/packages/dev-frontend/vite.config.ts
@@ -37,5 +37,8 @@ export default defineConfig({
globals: true,
environment: "jsdom",
setupFiles: "./src/setupTests.ts"
+ },
+ server: {
+ cors: false
}
});
diff --git a/yarn.lock b/yarn.lock
index f9af233c9..9f793a16a 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -1179,6 +1179,17 @@ __metadata:
languageName: node
linkType: hard
+"@ethersproject/experimental@npm:5.7.0":
+ version: 5.7.0
+ resolution: "@ethersproject/experimental@npm:5.7.0"
+ dependencies:
+ "@ethersproject/web": ^5.7.0
+ ethers: ^5.7.0
+ scrypt-js: 3.0.1
+ checksum: a4973371be9b984d5834df5d6fe3d4d1641204ec859a6d4e11e4a972a3a4a2dd7c0eae6a9a86b90f30faa3633a86ab153197bff6e853fb3ff8f847e0340a3c24
+ languageName: node
+ linkType: hard
+
"@ethersproject/hash@npm:5.7.0, @ethersproject/hash@npm:>=5.0.0-beta.128, @ethersproject/hash@npm:^5.0.4, @ethersproject/hash@npm:^5.7.0":
version: 5.7.0
resolution: "@ethersproject/hash@npm:5.7.0"
@@ -1736,6 +1747,7 @@ __metadata:
dependencies:
"@esbuild-plugins/node-modules-polyfill": ^0.2.2
"@ethersproject/abi": 5.7.0
+ "@ethersproject/experimental": 5.7.0
"@fortawesome/fontawesome-svg-core": 1.2.34
"@fortawesome/free-regular-svg-icons": 5.15.2
"@fortawesome/free-solid-svg-icons": 5.15.2
@@ -1758,7 +1770,7 @@ __metadata:
"@typescript-eslint/eslint-plugin": ^5.57.1
"@typescript-eslint/parser": ^5.57.1
"@vitejs/plugin-react-swc": ^3.0.0
- connectkit: 1.2.4
+ connectkit: ^1.3.0
cross-env: 7.0.3
eslint: ^8.38.0
eslint-plugin-react-hooks: ^4.6.0
@@ -1781,7 +1793,7 @@ __metadata:
typescript: ^5.0.4
vite: ^4.3.2
vitest: ^0.31.0
- wagmi: 0.12.13
+ wagmi: ^0.12.0
yalc: 1.0.0-pre.53
languageName: unknown
linkType: soft
@@ -8015,9 +8027,9 @@ __metadata:
languageName: node
linkType: hard
-"connectkit@npm:1.2.4":
- version: 1.2.4
- resolution: "connectkit@npm:1.2.4"
+"connectkit@npm:^1.3.0":
+ version: 1.3.0
+ resolution: "connectkit@npm:1.3.0"
dependencies:
buffer: ^6.0.3
detect-browser: ^5.3.0
@@ -8032,7 +8044,7 @@ __metadata:
react: 17.x || 18.x
react-dom: 17.x || 18.x
wagmi: 0.12.x
- checksum: f2ac12b7229542bd54d51e650d7fd9f3f64e969b1fb868056d19ec31e5caa532106b5553c694c4a681e22993c6db7d8d3e7978907d0f81b4ea7f8905e21049b7
+ checksum: b54fc6b7267be17d3fff69cb51f38d8537ca40d4611fb2685bb41501cac05b77e91cc35c1b5542958aae2d6d0bc1f987210ca3951b7e55e88da7b417f90195a0
languageName: node
linkType: hard
@@ -10412,7 +10424,7 @@ __metadata:
languageName: node
linkType: hard
-"ethers@npm:5.7.2, ethers@npm:^5.4.3, ethers@npm:^5.7.2":
+"ethers@npm:5.7.2, ethers@npm:^5.4.3, ethers@npm:^5.7.0, ethers@npm:^5.7.2":
version: 5.7.2
resolution: "ethers@npm:5.7.2"
dependencies:
@@ -22795,7 +22807,7 @@ __metadata:
languageName: node
linkType: hard
-"wagmi@npm:0.12.13":
+"wagmi@npm:^0.12.0":
version: 0.12.13
resolution: "wagmi@npm:0.12.13"
dependencies: