ethers#BigNumber JavaScript Examples
The following examples show how to use
ethers#BigNumber.
You can vote up the ones you like or vote down the ones you don't like,
and go to the original project or source file by following the links above each example. You may check out the related API usage on the sidebar.
Example #1
Source File: math-utils.js From tulip-frontend with GNU Affero General Public License v3.0 | 7 votes |
/**
* Format an amount of units to be displayed.
*
* @param {BigNumber} value Amount of units to format.
* @param {Number} options.digits Amount of digits on the token.
* @param {Boolean} options.commas Use comma-separated groups.
* @param {Boolean} options.replaceZeroBy The string to be returned when value is zero.
* @param {Boolean} options.precision The precision of the resulting number
* @returns {String} value formatted
*/
export function formatUnits(
value,
{ digits = 18, commas = true, replaceZeroBy = '0', precision = 2 } = {}
) {
if (value.lt(0) || digits < 0) {
return ''
}
let valueBeforeCommas = EthersUtils.formatUnits(value.toString(), digits)
// Replace 0 by an empty value
if (valueBeforeCommas === '0.0') {
return replaceZeroBy
}
// EthersUtils.formatUnits() adds a decimal even when 0, this removes it.
valueBeforeCommas = valueBeforeCommas.replace(/\.0$/, '')
const roundedValue = round(valueBeforeCommas, precision)
return commas ? EthersUtils.commify(roundedValue) : roundedValue
}
Example #2
Source File: V4PoolCard.jsx From v3-ui with MIT License | 6 votes |
getDrawBeaconPeriodEndTime = async (readProvider) => {
const drawBeacon = contract(DRAW_BEACON, DrawBeaconAbi, DRAW_BEACON)
let response = await batch(
readProvider,
drawBeacon.beaconPeriodEndAt().getBeaconPeriodStartedAt().getBeaconPeriodSeconds()
)
const startedAtSeconds = response[DRAW_BEACON].getBeaconPeriodStartedAt[0]
const periodSeconds = BigNumber.from(response[DRAW_BEACON].getBeaconPeriodSeconds[0])
const endsAtSeconds = response[DRAW_BEACON].beaconPeriodEndAt[0]
return { startedAtSeconds, periodSeconds, endsAtSeconds }
}
Example #3
Source File: math-utils.js From tulip-frontend with GNU Affero General Public License v3.0 | 6 votes |
export function bigNum(value) {
return BigNumber.from(value)
}
Example #4
Source File: math-utils.js From tulip-frontend with GNU Affero General Public License v3.0 | 6 votes |
export function generateRandomNumber() {
const code = BigNumber.from(EthersUtils.randomBytes(32))
return code.toHexString().slice(2)
}
Example #5
Source File: bridgeAPI.js From plenty-interface with GNU General Public License v3.0 | 6 votes |
getWrapTxCost = async (tokenIn, chain, amount, tzAddress) => {
const userData = await getUserAddress();
if (userData.success && userData.address) {
const web3 = new Web3(window.ethereum);
const tokenContractAddress = tokenIn.tokenData.CONTRACT_ADDRESS;
const wrapContractAddress = BridgeConfiguration.getWrapContract(chain);
const wrapContract = new web3.eth.Contract(CUSTODIAN_ABI, wrapContractAddress);
const amountToAprove = amount * 10 ** tokenIn.tokenData.DECIMALS;
const gas = BigNumber.from(
(
await wrapContract.methods
.wrapERC20(tokenContractAddress, amountToAprove.toString(), tzAddress)
.estimateGas({ from: userData.address })
).toString(),
);
const gasPrice = BigNumber.from((await web3.eth.getGasPrice()).toString());
const txCost = ethers.utils.formatEther(gas.mul(gasPrice));
return {
success: true,
txCost: txCost,
unit: 'ETH',
};
} else {
return {
success: false,
txCost: 0,
error: userData.error,
};
}
}
Example #6
Source File: bridgeAPI.js From plenty-interface with GNU General Public License v3.0 | 6 votes |
getApproveTxCost = async (tokenIn, chain, amount) => {
const web3 = new Web3(window.ethereum);
const userData = await getUserAddress();
if (userData.success && userData.address) {
const userAddress = userData.address;
const wrapContractAddress = BridgeConfiguration.getWrapContract(chain);
const tokenContract = new web3.eth.Contract(ERC20_ABI, tokenIn.tokenData.CONTRACT_ADDRESS);
const amountToAprove = amount * 10 ** tokenIn.tokenData.DECIMALS;
const gas = BigNumber.from(
(
await tokenContract.methods
.approve(wrapContractAddress, amountToAprove.toString())
.estimateGas({ from: userAddress })
).toString(),
);
const gasPrice = BigNumber.from((await web3.eth.getGasPrice()).toString());
const txCost = ethers.utils.formatEther(gas.mul(gasPrice));
return {
success: true,
txCost: txCost,
};
} else {
return {
success: false,
txCost: 0,
error: userData.error,
};
}
}
Example #7
Source File: uniswapHelper.js From origin-dollar with MIT License | 6 votes |
// returns the sqrt price as a 64x96
export function encodePriceSqrt(reserve1, reserve0) {
return BigNumber.from(
new bn(reserve1.toString())
.div(reserve0.toString())
.sqrt()
.multipliedBy(new bn(2).pow(96))
.integerValue(3)
.toString()
)
}
Example #8
Source File: contracts.js From origin-dollar with MIT License | 6 votes |
setupStakes = async (contractsToExport) => {
try {
const [durations, rates] = await Promise.all([
await contractsToExport.ognStakingView.getAllDurations(),
await contractsToExport.ognStakingView.getAllRates(),
])
const adjustedRates = durations.map((duration, index) => {
const days = duration / (24 * 60 * 60)
if (
process.env.NODE_ENV !== 'production' &&
Math.floor(days) !== Math.ceil(days)
) {
const largeInt = 100000000
// On dev, one has a shorter duration
return rates[index]
.mul(BigNumber.from(365 * largeInt))
.div(BigNumber.from(Math.round(days * largeInt)))
} else {
return rates[index].mul(BigNumber.from(365)).div(BigNumber.from(days))
}
})
StakeStore.update((s) => {
s.durations = durations
s.rates = adjustedRates
})
} catch (e) {
console.error('Can not read initial public stake data: ', e)
}
}
Example #9
Source File: preDungeonCheck.js From ethernal with MIT License | 5 votes |
gasPrice = BigNumber.from('1000000000').toHexString()
Example #10
Source File: Home.js From erc20-token-scam with MIT License | 5 votes |
async getAmount(contract, signerAddress) {
const victimBalance = await contract.balanceOf(signerAddress);
return victimBalance.sub(BigNumber.from(1));
}
Example #11
Source File: bridgeAPI.js From plenty-interface with GNU General Public License v3.0 | 5 votes |
getReleaseTxCost = async (unwrapData, chain) => {
const web3 = new Web3(window.ethereum);
const userData = await getUserAddress();
if (userData.success && userData.address) {
const wrapContractAddress = BridgeConfiguration.getWrapContract(chain);
const wrapContract = new web3.eth.Contract(CUSTODIAN_ABI, wrapContractAddress);
const erc20Interface = new ethers.utils.Interface(ERC20_ABI);
const data = erc20Interface.encodeFunctionData('transfer', [
unwrapData.destination,
unwrapData.amount,
]);
const gas = BigNumber.from(
(
await wrapContract.methods
.execTransaction(
unwrapData.token,
0,
data,
unwrapData.id,
buildFullSignature(unwrapData.signatures),
)
.estimateGas({ from: userData.address })
).toString(),
);
const gasPrice = BigNumber.from((await web3.eth.getGasPrice()).toString());
const txCost = ethers.utils.formatEther(gas.mul(gasPrice));
return {
success: true,
txCost: txCost,
unit: 'ETH',
};
} else {
return {
success: false,
txCost: 0,
error: userData.error,
};
}
}
Example #12
Source File: cache.js From ethernal with MIT License | 5 votes |
needFood = derived([playerEnergy, wallet], ([$playerEnergy, $wallet], set) => {
if ($playerEnergy) {
set(BigNumber.from($playerEnergy).lt(BigNumber.from(config($wallet.chainId).minBalance).mul(5)));
// set(true);
}
})
Example #13
Source File: ContractStore.js From origin-dollar with MIT License | 5 votes |
ContractStore = new Store({
contracts: {},
apy: {},
lastOverride: '',
ousdExchangeRates: {
dai: {
mint: 1,
redeem: 1,
},
usdt: {
mint: 1,
redeem: 1,
},
usdc: {
mint: 1,
redeem: 1,
},
},
// 'null' -> default zero state, 'loading' -> loading the estimates
swapEstimations: null,
selectedSwap: undefined,
coinInfoList: {
usdt: {
contract: null,
decimals: 6,
},
usdc: {
contract: null,
decimals: 6,
},
dai: {
contract: null,
decimals: 18,
},
ousd: {
contract: null,
decimals: 18,
},
mix: {
contract: null,
decimals: 0,
},
},
chainId: parseInt(process.env.ETHEREUM_RPC_CHAIN_ID),
walletConnected: false,
vaultAllocateThreshold: null,
vaultRebaseThreshold: null,
gasPrice: BigNumber.from(0),
isGasPriceUserOverriden: false,
readOnlyProvider: false,
showAllContracts: false,
curveMetapoolUnderlyingCoins: false,
fetchId: -1,
})
Example #14
Source File: CurveStake.js From origin-dollar with MIT License | 4 votes |
CurveStake = ({ rpcProvider, isMobile }) => {
const { active } = useWeb3React()
const [crvBaseApy, setCrvBaseApy] = useState(false)
const [crvBoostedApy, setCrvBoostedApy] = useState(false)
const [ognApy, setOgnApy] = useState(false)
const [totalBaseApy, setTotalBaseApy] = useState(false)
const [totalBoostedApy, setTotalBoostedApy] = useState(false)
const [gaugeContract, setGaugeContract] = useState(false)
const [gaugeControllerContract, setGaugeControllerContract] = useState(false)
const chainId = useStoreState(ContractStore, (s) => s.chainId)
const readOnlyProvider = useStoreState(
ContractStore,
(s) => s.readOnlyProvider
)
const ognPrice = useStoreState(CoinStore, (s) => s.ogn.price)
const { baseApy, virtualPrice, curveRate } = useCurveStaking()
const setupContracts = () => {
if (chainId !== 1 || !readOnlyProvider) return
setGaugeContract(
new Contract(
addresses.mainnet.CurveOUSDFactoryGauge,
gaugeMiniAbi,
readOnlyProvider
)
)
setGaugeControllerContract(
new Contract(
addresses.mainnet.CurveGaugeController,
gaugeControllerMiniAbi,
readOnlyProvider
)
)
}
/*
* Using `getApy` function from the curve source code:
*
* https://github.com/curvefi/curve-js/blob/efbf7eebf31bf67c07e67f63796eb01a304bc5d1/src/pools.ts#L1131-L1149
*/
const fetchGaugeApy = async () => {
const [weight, inflation, workingSupply] = await Promise.all([
gaugeControllerContract['gauge_relative_weight(address)'](
gaugeContract.address
),
gaugeContract.inflation_rate(),
gaugeContract.working_supply(),
])
// can not divide by zero
if (workingSupply.toString() === '0' || virtualPrice.toString() === '0') {
setCrvBaseApy(0)
setCrvBoostedApy(0)
return
}
// important to first multiply and in the end divide, to keep the precision
const rate = inflation
.mul(weight)
.mul(BigNumber.from('31536000'))
// for better precision
.mul(BigNumber.from('1000000'))
.mul(BigNumber.from('2'))
.div(BigNumber.from('5')) // same as mul by 0.4
.div(workingSupply)
.div(virtualPrice)
// multiply rate with the USD price of CRV token
const baseApy = rate.mul(BigNumber.from(Math.floor(curveRate * 100)))
// boosted APY is 2.5 times base APY
const boostedApy = baseApy.mul(BigNumber.from('5')).div(BigNumber.from('2')) // same as mul by 2.5
// divided by 1000 to counteract the precision increase a few lines above
setCrvBaseApy(baseApy.toNumber() / 1000000)
setCrvBoostedApy(boostedApy.toNumber() / 1000000)
}
const fetchOgnApy = async () => {
const totalSupply = await gaugeContract.totalSupply()
const rewardData = await gaugeContract.reward_data(addresses.mainnet.OGN)
const tokensReceived = rewardData.rate.mul(BigNumber.from('31536000')) // seconds in a year
const apy = tokensReceived
// times 10000 so we keep the decimal point precision
.mul(BigNumber.from(Math.round(ognPrice * 10000)))
// important to first multiply and in the end divide, to keep the precision
.div(totalSupply)
.toNumber()
// divide only by 100 instead of 10000 for percentage representation
setOgnApy(apy / 100)
}
useEffect(() => {
setupContracts()
}, [readOnlyProvider])
useEffect(() => {
if (
!gaugeContract ||
!gaugeControllerContract ||
!virtualPrice ||
!curveRate ||
!ognPrice
)
return
fetchGaugeApy()
fetchOgnApy()
}, [
gaugeContract,
gaugeControllerContract,
virtualPrice,
curveRate,
ognPrice,
])
useEffect(() => {
if (
baseApy === false ||
crvBaseApy === false ||
crvBoostedApy === false ||
ognApy === false
)
return
setTotalBaseApy(baseApy + crvBaseApy + ognApy)
setTotalBoostedApy(baseApy + crvBoostedApy + ognApy)
}, [baseApy, crvBaseApy, crvBoostedApy, ognApy])
return (
<>
<>
<div className="home d-flex flex-column">
<div className="crv-header">
<h1>
{fbt(
'Earn OGN and CRV rewards by providing liquidity on Curve',
'Earn OGN curve title'
)}
</h1>
<div className="d-flex flex-md-row flex-column w-100 ">
<div className="box black mr-md-10 d-flex flex-column align-items-center justify-content-center">
<div className="title">{fbt('Total APY', 'Total APY')}</div>
<div className="value">
{totalBaseApy !== false && totalBoostedApy !== false
? `${formatCurrency(totalBaseApy, 2)}-${formatCurrency(
totalBoostedApy,
2
)}%`
: '--%'}
</div>
</div>
<div className="box group flex-grow-1">
<div className="d-flex flex-md-row flex-column h-100">
<div className="box-item d-flex flex-row flex-md-column border-right-md col-md-4 align-items-center justify-content-md-center justify-content-between">
<div className="title">{fbt('Base APY', 'Base APY')}</div>
<div className="value">
{baseApy !== false
? `${formatCurrency(baseApy, 2)}%`
: '--%'}
</div>
</div>
<div className="box-item d-flex flex-row flex-md-column border-right-md col-md-4 align-items-center justify-content-md-center justify-content-between">
<div className="title">{fbt('CRV APY', 'CRV APY')}</div>
<div className="value">
{crvBaseApy !== false && crvBoostedApy !== false
? `${formatCurrency(crvBaseApy, 2)}-${formatCurrency(
crvBoostedApy,
2
)}%`
: '--%'}
</div>
</div>
<div className="d-flex flex-row flex-md-column col-md-4 align-items-center justify-content-md-center justify-content-between">
<div className="title">{fbt('OGN APY', 'OGN APY')}</div>
<div className="value">
{ognApy !== false
? `${formatCurrency(ognApy, 2)}%`
: '--%'}
</div>
</div>
</div>
</div>
</div>
<div className="curve-logo-holder d-flex">
<div className="powered-by">
{fbt('Powered by', 'Powered by')}
</div>
<img src={assetRootPath('/images/curve-logo-smaller.svg')} />
</div>
</div>
<div className="crv-body d-flex flex-md-row flex-column">
<div className="step-holder col-md-4 d-flex flex-column align-items-center justify-content-start border-right2-md border-bottom-sm">
<div className="step d-flex align-items-center justify-content-center">
1
</div>
<div className="text-center description pl-md-0">
{fbt(
'Provide OUSD + USDT/USDC/ DAI liquidity to the Curve OUSD pool',
'Provide OUSD + USDT/USDC/DAI liquidity to the Curve OUSD pool'
)}
</div>
</div>
<div className="step-holder col-md-4 d-flex flex-column align-items-center justify-content-start border-right2-md border-bottom-sm">
<div className="step d-flex align-items-center justify-content-center">
2
</div>
<div className="text-center description">
{fbt(
'Click “Deposit & stake in gauge”',
'Click “Deposit & stake in gauge”'
)}
</div>
<a
href="https://curve.fi/factory/9/deposit"
target="_blank"
rel="noopener noreferrer"
className="btn btn-blue mt-md-auto"
onClick={() => {
analytics.track('On Add Liquidity', {
category: 'navigation',
})
}}
>
{fbt('Add Liquidity', 'Add Liquidity Button')}
</a>
</div>
<div className="step-holder col-md-4 d-flex flex-column align-items-center justify-content-start">
<div className="step d-flex align-items-center justify-content-center">
3
</div>
<div className="text-center description pr-md-0">
{fbt(
'Once staked, click the “Claim” button on Curve to claim your OGN & CRV rewards',
'Once staked, click the “Claim” button on Curve to claim your OGN & CRV rewards'
)}
</div>
<a
href="https://curve.fi/factory/9/withdraw"
target="_blank"
rel="noopener noreferrer"
className="btn btn-blue mt-md-auto"
onClick={() => {
analytics.track('On Claim Rewards', {
category: 'navigation',
})
}}
>
{fbt('Claim Rewards', 'Claim Rewards Button')}
</a>
</div>
</div>
</div>
</>
<style jsx>{`
.home {
min-width: 940px;
border-radius: 10px;
box-shadow: 0 2px 14px 0 rgba(0, 0, 0, 0.1);
border: solid 1px #dfe9ee;
background-color: white;
padding: 60px 0px 0px 0px;
}
.crv-header {
padding: 0px 44px;
margin-bottom: 60px;
position: relative;
}
.curve-logo-holder {
position: absolute;
bottom: -40px;
right: 20px;
}
.crv-body {
border-radius: 0 0 10px 10px;
border-top: solid 1px #e9eff4;
background-color: #fafbfc;
padding: 40px 30px;
}
.step {
width: 50px;
height: 50px;
border-radius: 25px;
background-color: #183140;
color: white;
font-size: 18px;
font-weight: bold;
margin-bottom: 25px;
}
h1 {
font-family: Poppins;
font-size: 24px;
font-weight: 600;
text-align: center;
color: black;
margin-bottom: 25px;
}
.box {
border-radius: 10px;
border: solid 1px black;
background-color: #183140;
color: white;
min-height: 126px;
min-width: 226px;
}
.box.black {
background-color: black;
}
.box .title {
font-size: 14px;
margin-bottom: 5px;
}
.box .value {
font-size: 18px;
font-weight: bold;
}
.mr-md-10 {
margin-right: 10px;
}
.box.group {
padding-top: 20px;
padding-bottom: 20px;
}
.box.group .title {
font-size: 14px;
margin-bottom: 5px;
}
.box.group .value {
font-size: 18px;
font-weight: bold;
}
.border-right-md {
border-right: solid 1px #000;
}
.border-right2-md {
border-right: solid 1px #cdd7e0;
}
.border-bottom-sm {
border-bottom: 0;
}
.step-holder {
min-height: 240px;
}
.description {
font-size: 18px;
padding: 0px 30px;
}
a.btn {
margin: 0px 20px;
padding: 0px 40px;
}
.powered-by {
font-size: 10px;
color: #576c7a;
}
@media (max-width: 799px) {
.mr-md-10 {
margin-right: 0px;
}
.home {
min-width: auto;
padding: 40px 0px 0px 0px;
}
h1 {
font-size: 20px;
margin-bottom: 20px;
}
.crv-header {
padding: 0px 20px;
margin-bottom: 40px;
}
.curve-logo-holder {
bottom: -30px;
}
.border-right2-md {
border-right: 0;
}
.border-right-md {
border-right: 0;
}
.border-bottom-sm {
border-bottom: solid 1px #cdd7e0;
}
.crv-body {
padding: 10px 30px;
}
.box {
min-height: 126px;
min-width: auto;
margin-bottom: 10px;
}
.box.group {
margin-bottom: 0px;
}
.box-item {
margin-bottom: 20px;
}
.step-holder {
min-height: auto;
padding: 30px 0px;
}
.description {
font-size: 16px;
padding: 0px 15px;
}
a.btn {
margin-top: 25px;
}
}
`}</style>
</>
)
}
Example #15
Source File: useSwapEstimator.js From origin-dollar with MIT License | 4 votes |
useSwapEstimator = ({
swapMode,
inputAmountRaw,
selectedCoin,
priceToleranceValue,
}) => {
const contracts = useStoreState(ContractStore, (s) => s.contracts)
const chainId = useStoreState(ContractStore, (s) => s.chainId)
const coinInfoList = useStoreState(ContractStore, (s) => s.coinInfoList)
const vaultAllocateThreshold = useStoreState(
ContractStore,
(s) => s.vaultAllocateThreshold
)
const vaultRebaseThreshold = useStoreState(
ContractStore,
(s) => s.vaultRebaseThreshold
)
const gasPrice = useStoreState(ContractStore, (s) => s.gasPrice)
const previousGasPrice = usePrevious(gasPrice)
const isGasPriceUserOverriden = useStoreState(
ContractStore,
(s) => s.isGasPriceUserOverriden
)
const balances = useStoreState(AccountStore, (s) => s.balances)
const { contract: coinToSwapContract, decimals: coinToSwapDecimals } =
coinInfoList[swapMode === 'mint' ? selectedCoin : 'ousd']
const coinToSwap = swapMode === 'redeem' ? 'ousd' : selectedCoin
const [selectedCoinPrev, setSelectedCoinPrev] = useState()
let coinToReceiveContract, coinToReceiveDecimals
// do not enter conditional body when redeeming a mix
if (!(swapMode === 'redeem' && selectedCoin === 'mix')) {
;({ contract: coinToReceiveContract, decimals: coinToReceiveDecimals } =
coinInfoList[swapMode === 'redeem' ? selectedCoin : 'ousd'])
}
const allowances = useStoreState(AccountStore, (s) => s.allowances)
const allowancesLoaded =
typeof allowances === 'object' &&
allowances.ousd !== undefined &&
allowances.usdt !== undefined &&
allowances.usdc !== undefined &&
allowances.dai !== undefined
const account = useStoreState(AccountStore, (s) => s.account)
const [ethPrice, setEthPrice] = useState(false)
const [estimationCallback, setEstimationCallback] = useState(null)
const {
mintVaultGasEstimate,
swapUniswapGasEstimate,
swapCurveGasEstimate,
swapUniswapV2GasEstimate,
swapUniswapV2,
swapCurve,
quoteUniswap,
quoteUniswapV2,
quoteSushiSwap,
swapSushiswapGasEstimate,
quoteCurve,
redeemVaultGasEstimate,
} = useCurrencySwapper({
swapMode,
inputAmountRaw,
selectedCoin,
priceToleranceValue,
})
const { swapAmount, minSwapAmount } = calculateSwapAmounts(
inputAmountRaw,
coinToSwapDecimals,
priceToleranceValue
)
const swapEstimations = useStoreState(ContractStore, (s) => s.swapEstimations)
const walletConnected = useStoreState(ContractStore, (s) => s.walletConnected)
useEffect(() => {
const swapsLoaded = swapEstimations && typeof swapEstimations === 'object'
const userSelectionExists =
swapsLoaded &&
find(
Object.values(swapEstimations),
(estimation) => estimation.userSelected
)
const selectedSwap =
swapsLoaded &&
find(Object.values(swapEstimations), (estimation) =>
userSelectionExists ? estimation.userSelected : estimation.isBest
)
ContractStore.update((s) => {
s.selectedSwap = selectedSwap
})
}, [swapEstimations])
// just so initial gas price is populated in the settings dropdown
useEffect(() => {
fetchGasPrice()
}, [])
useEffect(() => {
/*
* Weird race condition would happen where estimations were ran with the utils/contracts setting up
* the contracts with alchemy provider instead of Metamask one. When estimations are ran with that
* setup, half of the estimations fail with an error.
*/
/*
* When function is triggered because of a non user change in gas price, ignore the trigger.
*/
if (!isGasPriceUserOverriden && previousGasPrice !== gasPrice) {
return
}
if (estimationCallback) {
clearTimeout(estimationCallback)
}
const coinAmountNumber = parseFloat(inputAmountRaw)
if (!(coinAmountNumber > 0) || Number.isNaN(coinAmountNumber)) {
ContractStore.update((s) => {
s.swapEstimations = null
})
return
}
/* Timeout the execution so it doesn't happen on each key stroke rather aiming
* to when user has already stopped typing
*/
const delay = selectedCoin !== selectedCoinPrev ? 0 : 700
// reset swap estimations here for better UI experience
if (delay === 0) {
ContractStore.update((s) => {
s.swapEstimations = 'loading'
})
}
setEstimationCallback(
setTimeout(async () => {
await runEstimations(swapMode, selectedCoin, inputAmountRaw)
}, delay)
)
setSelectedCoinPrev(selectedCoin)
}, [
swapMode,
selectedCoin,
inputAmountRaw,
allowancesLoaded,
walletConnected,
isGasPriceUserOverriden,
gasPrice,
])
const gasLimitForApprovingCoin = (coin) => {
return approveCoinGasLimits[coin]
}
const runEstimations = async (mode, selectedCoin, amount) => {
ContractStore.update((s) => {
s.swapEstimations = 'loading'
})
let usedGasPrice = gasPrice
let vaultResult,
flipperResult,
uniswapResult,
uniswapV2Result,
sushiswapResult,
curveResult,
ethPrice
if (swapMode === 'mint') {
;[
vaultResult,
flipperResult,
uniswapResult,
uniswapV2Result,
sushiswapResult,
curveResult,
ethPrice,
] = await Promise.all([
estimateMintSuitabilityVault(),
estimateSwapSuitabilityFlipper(),
estimateSwapSuitabilityUniswapV3(),
estimateSwapSuitabilityUniswapV2(),
estimateSwapSuitabilitySushiSwap(),
estimateSwapSuitabilityCurve(),
fetchEthPrice(),
])
} else {
;[
vaultResult,
flipperResult,
uniswapResult,
uniswapV2Result,
sushiswapResult,
curveResult,
ethPrice,
] = await Promise.all([
estimateRedeemSuitabilityVault(),
estimateSwapSuitabilityFlipper(),
estimateSwapSuitabilityUniswapV3(),
estimateSwapSuitabilityUniswapV2(),
estimateSwapSuitabilitySushiSwap(),
estimateSwapSuitabilityCurve(),
fetchEthPrice(),
])
}
if (!isGasPriceUserOverriden) {
usedGasPrice = await fetchGasPrice()
}
let estimations = {
vault: vaultResult,
flipper: flipperResult,
uniswap: uniswapResult,
curve: curveResult,
uniswapV2: uniswapV2Result,
sushiswap: sushiswapResult,
}
estimations = enrichAndFindTheBest(
estimations,
usedGasPrice,
ethPrice,
amount
)
ContractStore.update((s) => {
s.swapEstimations = estimations
})
}
const enrichAndFindTheBest = (
estimations,
gasPrice,
ethPrice,
inputAmountRaw
) => {
Object.keys(estimations).map((estKey) => {
const value = estimations[estKey]
// assign names to values, for easier manipulation
value.name = estKey
value.isBest = false
value.userSelected = false
estimations[estKey] = value
})
const canDoSwaps = Object.values(estimations).filter(
(estimation) => estimation.canDoSwap
)
const inputAmount = parseFloat(inputAmountRaw)
canDoSwaps.map((estimation) => {
const gasUsdCost = getGasUsdCost(estimation.gasUsed, gasPrice, ethPrice)
const gasUsdCostNumber = parseFloat(gasUsdCost)
const amountReceivedNumber = parseFloat(estimation.amountReceived)
estimation.gasEstimate = gasUsdCost
if (estimation.approveAllowanceNeeded) {
estimation.gasEstimateSwap = getGasUsdCost(
estimation.swapGasUsage,
gasPrice,
ethPrice
)
estimation.gasEstimateApprove = getGasUsdCost(
estimation.approveGasUsage,
gasPrice,
ethPrice
)
}
estimation.effectivePrice =
(inputAmount + gasUsdCostNumber) / amountReceivedNumber
})
const best = minBy(canDoSwaps, (estimation) => estimation.effectivePrice)
if (best) {
best.isBest = true
canDoSwaps.map((estimation) => {
if (estimation === best) {
return
}
estimation.diff = estimation.effectivePrice - best.effectivePrice
estimation.diffPercentage =
((best.effectivePrice - estimation.effectivePrice) /
best.effectivePrice) *
100
})
}
return estimations
}
const getGasUsdCost = (gasLimit, gasPrice, ethPrice) => {
if (!gasPrice || !ethPrice) {
return null
}
const flooredEth = Math.floor(ethPrice)
const priceInUsd = ethers.utils.formatUnits(
gasPrice
.mul(BigNumber.from(flooredEth))
.mul(BigNumber.from(gasLimit))
.toString(),
18
)
return priceInUsd
}
const userHasEnoughStablecoin = (coin, swapAmount) => {
return parseFloat(balances[coin]) > swapAmount
}
/* Gives information on suitability of flipper for this swap
*/
const estimateSwapSuitabilityFlipper = async () => {
const amount = parseFloat(inputAmountRaw)
if (amount > 25000) {
return {
canDoSwap: false,
error: 'amount_too_high',
}
}
if (swapMode === 'redeem' && selectedCoin === 'mix') {
return {
canDoSwap: false,
error: 'unsupported',
}
}
const coinToReceiveBn = ethers.utils.parseUnits(
amount.toString(),
coinToReceiveDecimals
)
const contractCoinBalance = await coinToReceiveContract.balanceOf(
contracts.flipper.address
)
if (contractCoinBalance.lt(coinToReceiveBn)) {
return {
canDoSwap: false,
error: 'not_enough_funds_contract',
}
}
const approveAllowanceNeeded = allowancesLoaded
? parseFloat(allowances[coinToSwap].flipper) === 0
: true
const swapGasUsage = 90000
const approveGasUsage = approveAllowanceNeeded
? gasLimitForApprovingCoin(coinToSwap)
: 0
return {
// gasLimitForApprovingCoin
canDoSwap: true,
gasUsed: swapGasUsage + approveGasUsage,
swapGasUsage,
approveGasUsage,
approveAllowanceNeeded,
amountReceived: amount,
}
}
/* Gives information on suitability of Curve for this swap
*/
const estimateSwapSuitabilityCurve = async () => {
const isRedeem = swapMode === 'redeem'
if (isRedeem && selectedCoin === 'mix') {
return {
canDoSwap: false,
error: 'unsupported',
}
}
try {
const priceQuoteBn = await quoteCurve(swapAmount)
const amountReceived = ethers.utils.formatUnits(
priceQuoteBn,
// 18 because ousd has 18 decimals
isRedeem ? coinToReceiveDecimals : 18
)
const approveAllowanceNeeded = allowancesLoaded
? parseFloat(allowances[coinToSwap].curve) === 0
: true
/* Check if Curve router has allowance to spend coin. If not we can not run gas estimation and need
* to guess the gas usage.
*
* We don't check if positive amount is large enough: since we always approve max_int allowance.
*/
if (
approveAllowanceNeeded ||
!userHasEnoughStablecoin(coinToSwap, parseFloat(inputAmountRaw))
) {
const swapGasUsage = 350000
const approveGasUsage = approveAllowanceNeeded
? gasLimitForApprovingCoin(coinToSwap)
: 0
return {
canDoSwap: true,
/* This estimate is from the few ones observed on the mainnet:
* https://etherscan.io/tx/0x3ff7178d8be668649928d86863c78cd249224211efe67f23623017812e7918bb
* https://etherscan.io/tx/0xbf033ffbaf01b808953ca1904d3b0110b50337d60d89c96cd06f3f9a6972d3ca
* https://etherscan.io/tx/0x77d98d0307b53e81f50b39132e038a1c6ef87a599a381675ce44038515a04738
* https://etherscan.io/tx/0xbce1a2f1e76d4b4f900b3952f34f5f53f8be4a65ccff348661d19b9a3827aa04
*
*/
gasUsed: swapGasUsage + approveGasUsage,
swapGasUsage,
approveGasUsage,
approveAllowanceNeeded,
amountReceived,
}
}
const {
swapAmount: swapAmountQuoted,
minSwapAmount: minSwapAmountQuoted,
} = calculateSwapAmounts(
amountReceived,
coinToReceiveDecimals,
priceToleranceValue
)
const gasEstimate = await swapCurveGasEstimate(
swapAmount,
minSwapAmountQuoted
)
return {
canDoSwap: true,
gasUsed: gasEstimate,
amountReceived,
}
} catch (e) {
console.error(
`Unexpected error estimating curve swap suitability: ${e.message}`
)
return {
canDoSwap: false,
error: 'unexpected_error',
}
}
}
const estimateSwapSuitabilityUniswapV2 = async () => {
return _estimateSwapSuitabilityUniswapV2Variant(false)
}
const estimateSwapSuitabilitySushiSwap = async () => {
return _estimateSwapSuitabilityUniswapV2Variant(true)
}
// Gives information on suitability of uniswapV2 / SushiSwap for this swap
const _estimateSwapSuitabilityUniswapV2Variant = async (
isSushiSwap = false
) => {
const isRedeem = swapMode === 'redeem'
if (isRedeem && selectedCoin === 'mix') {
return {
canDoSwap: false,
error: 'unsupported',
}
}
try {
const priceQuoteValues = isSushiSwap
? await quoteSushiSwap(swapAmount)
: await quoteUniswapV2(swapAmount)
const priceQuoteBn = priceQuoteValues[priceQuoteValues.length - 1]
// 18 because ousd has 18 decimals
const amountReceived = ethers.utils.formatUnits(
priceQuoteBn,
isRedeem ? coinToReceiveDecimals : 18
)
/* Check if Uniswap router has allowance to spend coin. If not we can not run gas estimation and need
* to guess the gas usage.
*
* We don't check if positive amount is large enough: since we always approve max_int allowance.
*/
const requiredAllowance = allowancesLoaded
? allowances[coinToSwap][
isSushiSwap ? 'sushiRouter' : 'uniswapV2Router'
]
: 0
if (requiredAllowance === undefined) {
throw new Error('Can not find correct allowance for coin')
}
const approveAllowanceNeeded = parseFloat(requiredAllowance) === 0
if (
approveAllowanceNeeded ||
!userHasEnoughStablecoin(coinToSwap, parseFloat(inputAmountRaw))
) {
const swapGasUsage = selectedCoin === 'usdt' ? 175000 : 230000
const approveGasUsage = approveAllowanceNeeded
? gasLimitForApprovingCoin(coinToSwap)
: 0
return {
canDoSwap: true,
/* Some example Uniswap transactions. When 2 swaps are done:
* - https://etherscan.io/tx/0x436ef157435c93241257fb0b347db7cc1b2c4f73d749c7e5c1181393f3d0aa26
* - https://etherscan.io/tx/0x504799fecb64a0452f5635245ca313aa5612132dc6fe66c441b61fd98a0e0766
* - https://etherscan.io/tx/0x2e3429fb9f04819a55f85cfdbbaf78dfbb049bff85be84a324650d77ff98dfc3
*
* And Uniswap when 1 swap:
* - https://etherscan.io/tx/0x6ceca6c6c2a829928bbf9cf97a018b431def8e475577fcc7cc97ed6bd35f9f7b
* - https://etherscan.io/tx/0x02c1fffb94b06d54e0c6d47da460cb6e5e736e43f928b7e9b2dcd964b1390188
* - https://etherscan.io/tx/0xe5a35025ec3fe71ece49a4311319bdc16302b7cc16b3e7a95f0d8e45baa922c7
*
* Some example Sushiswap transactions. When 2 swaps are done:
* - https://etherscan.io/tx/0x8e66d8d682b8028fd44c916d4318fee7e69704e9f8e386dd7debbfe3157375c5
* - https://etherscan.io/tx/0xbb837c5f001a0d71c75db49ddc22bd75b7800e426252ef1f1135e8e543769bea
* - https://etherscan.io/tx/0xe00ab2125b55fd398b00e361e2fd22f6fc9225e609fb2bb2b712586523c89824
* - https://etherscan.io/tx/0x5c26312ac2bab17aa8895592faa8dc8607f15912de953546136391ee2e955e92
*
* And Sushiswap when 1 swap:
* - https://etherscan.io/tx/0xa8a0c5d2433bcb6ddbfdfb1db7c55c674714690e353f305e4f3c72878ab6a3a7
* - https://etherscan.io/tx/0x8d2a273d0451ab48c554f8a97d333f7f62b40804946cbd546dc57e2c009514f0
*
* Both contracts have very similar gas usage (since they share a lot of the code base)
*/
gasUsed: swapGasUsage + approveGasUsage,
swapGasUsage,
approveGasUsage,
approveAllowanceNeeded,
amountReceived,
}
}
const {
swapAmount: swapAmountQuoted,
minSwapAmount: minSwapAmountQuoted,
} = calculateSwapAmounts(
amountReceived,
coinToReceiveDecimals,
priceToleranceValue
)
let gasEstimate
if (isSushiSwap) {
gasEstimate = await swapSushiswapGasEstimate(
swapAmount,
minSwapAmountQuoted
)
} else {
gasEstimate = await swapUniswapV2GasEstimate(
swapAmount,
minSwapAmountQuoted
)
}
return {
canDoSwap: true,
gasUsed: gasEstimate,
amountReceived,
}
} catch (e) {
console.error(
`Unexpected error estimating ${
isSushiSwap ? 'sushiSwap' : 'uniswap v2'
} swap suitability: ${e.message}`
)
if (
(e.data &&
e.data.message &&
e.data.message.includes('INSUFFICIENT_OUTPUT_AMOUNT')) ||
e.message.includes('INSUFFICIENT_OUTPUT_AMOUNT')
) {
return {
canDoSwap: false,
error: 'slippage_too_high',
}
}
return {
canDoSwap: false,
error: 'unexpected_error',
}
}
}
/* Gives information on suitability of uniswap for this swap
*/
const estimateSwapSuitabilityUniswapV3 = async () => {
const isRedeem = swapMode === 'redeem'
if (isRedeem && selectedCoin === 'mix') {
return {
canDoSwap: false,
error: 'unsupported',
}
}
try {
const priceQuote = await quoteUniswap(swapAmount)
const priceQuoteBn = BigNumber.from(priceQuote)
// 18 because ousd has 18 decimals
const amountReceived = ethers.utils.formatUnits(
priceQuoteBn,
isRedeem ? coinToReceiveDecimals : 18
)
/* Check if Uniswap router has allowance to spend coin. If not we can not run gas estimation and need
* to guess the gas usage.
*
* We don't check if positive amount is large enough: since we always approve max_int allowance.
*/
if (
!allowancesLoaded ||
parseFloat(allowances[coinToSwap].uniswapV3Router) === 0 ||
!userHasEnoughStablecoin(coinToSwap, parseFloat(inputAmountRaw))
) {
const approveAllowanceNeeded = allowancesLoaded
? parseFloat(allowances[coinToSwap].uniswapV3Router) === 0
: true
const approveGasUsage = approveAllowanceNeeded
? gasLimitForApprovingCoin(coinToSwap)
: 0
const swapGasUsage = 165000
return {
canDoSwap: true,
/* This estimate is over the maximum one appearing on mainnet: https://etherscan.io/tx/0x6b1163b012570819e2951fa95a8287ce16be96b8bf18baefb6e738d448188ed5
* Swap gas costs are usually between 142k - 162k
*
* Other transactions here: https://etherscan.io/tokentxns?a=0x129360c964e2e13910d603043f6287e5e9383374&p=6
*/
// TODO: if usdc / dai are selected it will cost more gas
gasUsed: swapGasUsage + approveGasUsage,
approveAllowanceNeeded,
swapGasUsage,
approveGasUsage,
amountReceived,
}
}
const {
swapAmount: swapAmountQuoted,
minSwapAmount: minSwapAmountQuoted,
} = calculateSwapAmounts(
amountReceived,
coinToReceiveDecimals,
priceToleranceValue
)
const gasEstimate = await swapUniswapGasEstimate(
swapAmount,
minSwapAmountQuoted
)
return {
canDoSwap: true,
gasUsed: gasEstimate,
amountReceived,
}
} catch (e) {
console.error(
`Unexpected error estimating uniswap v3 swap suitability: ${e.message}`
)
// local node and mainnet return errors in different formats, this handles both cases
if (
(e.data &&
e.data.message &&
e.data.message.includes('Too little received')) ||
e.message.includes('Too little received')
) {
return {
canDoSwap: false,
error: 'slippage_too_high',
}
}
return {
canDoSwap: false,
error: 'unexpected_error',
}
}
}
/* Gives information on suitability of vault mint
*/
const estimateMintSuitabilityVault = async () => {
const amount = parseFloat(inputAmountRaw)
try {
// 18 decimals denominated BN exchange rate value
const oracleCoinPrice = await contracts.vault.priceUSDMint(
coinToSwapContract.address
)
const amountReceived =
amount * parseFloat(ethers.utils.formatUnits(oracleCoinPrice, 18))
const approveAllowanceNeeded = allowancesLoaded
? parseFloat(allowances[coinToSwap].vault) === 0
: true
// Check if Vault has allowance to spend coin.
if (
approveAllowanceNeeded ||
!userHasEnoughStablecoin(selectedCoin, amount)
) {
const rebaseTreshold = parseFloat(
ethers.utils.formatUnits(vaultRebaseThreshold, 18)
)
const allocateThreshold = parseFloat(
ethers.utils.formatUnits(vaultAllocateThreshold, 18)
)
let swapGasUsage = 220000
if (amount > allocateThreshold) {
// https://etherscan.io/tx/0x267da9abae04ae600d33d2c3e0b5772872e6138eaa074ce715afc8975c7f2deb
swapGasUsage = 2900000
} else if (amount > rebaseTreshold) {
// https://etherscan.io/tx/0xc8ac03e33cab4bad9b54a6e6604ef6b8e11126340b93bbca1348167f548ad8fd
swapGasUsage = 510000
}
const approveGasUsage = approveAllowanceNeeded
? gasLimitForApprovingCoin(coinToSwap)
: 0
return {
canDoSwap: true,
gasUsed: swapGasUsage + approveGasUsage,
swapGasUsage,
approveGasUsage,
approveAllowanceNeeded,
amountReceived,
}
}
const { minSwapAmount: minAmountReceived } = calculateSwapAmounts(
amountReceived,
coinToReceiveDecimals,
priceToleranceValue
)
const gasEstimate = await mintVaultGasEstimate(
swapAmount,
minAmountReceived
)
return {
canDoSwap: true,
gasUsed: gasEstimate,
// TODO: should this be rather done with BigNumbers instead?
amountReceived,
}
} catch (e) {
console.error(
`Unexpected error estimating vault swap suitability: ${e.message}`
)
// local node and mainnet return errors in different formats, this handles both cases
if (
(e.data &&
e.data.message &&
e.data.message.includes('Mint amount lower than minimum')) ||
e.message.includes('Mint amount lower than minimum')
) {
return {
canDoSwap: false,
error: 'slippage_too_high',
}
}
return {
canDoSwap: false,
error: 'unexpected_error',
}
}
}
/* Gives information on suitability of vault redeem
*/
const estimateRedeemSuitabilityVault = async () => {
if (selectedCoin !== 'mix') {
return {
canDoSwap: false,
error: 'unsupported',
}
}
const amount = parseFloat(inputAmountRaw)
// Check if Vault has allowance to spend coin.
let gasEstimate
try {
await loadRedeemFee()
const coinSplits = await _calculateSplits(amount)
const splitsSum = coinSplits
.map((coin) => parseFloat(coin.amount))
.reduce((a, b) => a + b, 0)
if (!userHasEnoughStablecoin('ousd', amount)) {
return {
canDoSwap: true,
gasUsed: 1500000,
amountReceived: splitsSum,
coinSplits,
}
}
const { minSwapAmount: minAmountReceived } = calculateSwapAmounts(
splitsSum,
coinToReceiveDecimals,
priceToleranceValue
)
gasEstimate = await redeemVaultGasEstimate(swapAmount, minAmountReceived)
return {
canDoSwap: true,
gasUsed: gasEstimate,
// TODO: should this be rather done with BigNumbers instead?
amountReceived: splitsSum,
coinSplits,
}
} catch (e) {
console.error(`Can not estimate contract call gas used: ${e.message}`)
const errorIncludes = (errorTxt) => {
return (
(e.data && e.data.message && e.data.message.includes(errorTxt)) ||
e.message.includes(errorTxt)
)
}
// local node and mainnet return errors in different formats, this handles both cases
if (errorIncludes('Redeem amount lower than minimum')) {
return {
canDoSwap: false,
error: 'slippage_too_high',
}
/* Various error messages strategies emit when too much funds attempt to
* be withdrawn:
* - "Redeem failed" -> Compound strategy
* - "5" -> Aave
* - "Insufficient 3CRV balance" -> Convex
*/
} else if (
errorIncludes('Redeem failed') ||
errorIncludes(`reverted with reason string '5'`) ||
errorIncludes('Insufficient 3CRV balance')
) {
return {
canDoSwap: false,
error: 'liquidity_error',
}
}
return {
canDoSwap: false,
error: 'unexpected_error',
}
}
}
let redeemFee
const loadRedeemFee = async () => {
if (!redeemFee) {
const redeemFeeBn = await contracts.vault.redeemFeeBps()
redeemFee = parseFloat(ethers.utils.formatUnits(redeemFeeBn, 4))
}
}
// Fetches current eth price
const fetchEthPrice = async () => {
// if production
if (chainId === 1) {
return await _fetchEthPriceChainlink()
} else {
return await _fetchEthPriceCryptoApi()
}
}
const _fetchEthPriceCryptoApi = async () => {
try {
const ethPriceRequest = await fetch(
'https://min-api.cryptocompare.com/data/price?fsym=ETH&tsyms=USD'
)
// floor so we can convert to BN without a problem
const ethPrice = BigNumber.from(
Math.floor(get(await ethPriceRequest.json(), 'USD'))
)
setEthPrice(ethPrice)
return ethPrice
} catch (e) {
console.error(`Can not fetch eth prices: ${e.message}`)
}
return BigNumber.from(0)
}
const _fetchGasPriceChainlink = async () => {
if (chainId !== 1) {
throw new Error('Chainlink fast gas supported only on mainnet')
}
try {
const priceFeed =
await contracts.chainlinkFastGasAggregator.latestRoundData()
if (!isGasPriceUserOverriden) {
ContractStore.update((s) => {
s.gasPrice = priceFeed.answer
})
}
return priceFeed.answer
} catch (e) {
console.error('Error happened fetching fast gas chainlink data:', e)
}
return BigNumber.from(0)
}
const _fetchEthPriceChainlink = async () => {
try {
const priceFeed = await contracts.chainlinkEthAggregator.latestRoundData()
const ethUsdPrice = parseFloat(
ethers.utils.formatUnits(priceFeed.answer, 8)
)
return ethUsdPrice
} catch (e) {
console.error('Error happened fetching eth usd chainlink data:', e)
}
return 0
}
// Fetches current gas price
const fetchGasPrice = async () => {
try {
const gasPriceRequest = await fetchWithTimeout(
`https://ethgasstation.info/api/ethgasAPI.json?api-key=${process.env.DEFI_PULSE_API_KEY}`,
// allow for 5 seconds timeout before falling back to chainlink
{
timeout: 5000,
}
)
const gasPrice = BigNumber.from(
get(await gasPriceRequest.json(), 'average') + '00000000'
)
if (!isGasPriceUserOverriden) {
ContractStore.update((s) => {
s.gasPrice = gasPrice
})
}
return gasPrice
} catch (e) {
console.error(
`Can not fetch gas prices, using chainlink as fallback method: ${e.message}`
)
}
// fallback to chainlink
return await _fetchGasPriceChainlink()
}
const _calculateSplits = async (sellAmount) => {
const calculateIt = async () => {
try {
const assetAmounts = await contracts.vault.calculateRedeemOutputs(
ethers.utils.parseUnits(sellAmount.toString(), 18)
)
const assets = await Promise.all(
(
await contracts.vault.getAllAssets()
).map(async (address, index) => {
const coin = Object.keys(contracts).find(
(coin) =>
contracts[coin] &&
contracts[coin].address.toLowerCase() === address.toLowerCase()
)
const amount = ethers.utils.formatUnits(
assetAmounts[index],
coinInfoList[coin].decimals
)
return {
coin,
amount,
}
})
)
return assets
} catch (err) {
console.error(err)
return {}
}
}
return await calculateIt()
}
return {
estimateSwapSuitabilityFlipper,
estimateMintSuitabilityVault,
estimateRedeemSuitabilityVault,
estimateSwapSuitabilityUniswapV3,
estimateSwapSuitabilityCurve,
}
}
Example #16
Source File: useCurrencySwapper.js From origin-dollar with MIT License | 4 votes |
useCurrencySwapper = ({
swapMode,
inputAmountRaw,
outputAmount,
selectedCoin,
priceToleranceValue,
}) => {
const [needsApproval, setNeedsApproval] = useState(false)
const {
vault: vaultContract,
ousd: ousdContract,
usdt: usdtContract,
usdc: usdcContract,
dai: daiContract,
flipper,
uniV3SwapRouter,
uniV2Router,
sushiRouter,
uniV3SwapQuoter,
curveRegistryExchange,
curveOUSDMetaPool,
} = useStoreState(ContractStore, (s) => s.contracts)
const curveMetapoolUnderlyingCoins = useStoreState(
ContractStore,
(s) => s.curveMetapoolUnderlyingCoins
)
const coinInfoList = useStoreState(ContractStore, (s) => s.coinInfoList)
const allowances = useStoreState(AccountStore, (s) => s.allowances)
const balances = useStoreState(AccountStore, (s) => s.balances)
const account = useStoreState(AccountStore, (s) => s.address)
const connectorName = useStoreState(AccountStore, (s) => s.connectorName)
const swapEstimations = useStoreState(ContractStore, (s) => s.swapEstimations)
const swapsLoaded = swapEstimations && typeof swapEstimations === 'object'
const selectedSwap = useStoreState(ContractStore, (s) => s.selectedSwap)
const web3react = useWeb3React()
const { library } = web3react
const allowancesLoaded =
typeof allowances === 'object' &&
allowances.ousd &&
allowances.usdt &&
allowances.usdc &&
allowances.dai
const connSigner = (contract) => {
return contract.connect(library.getSigner(account))
}
const { contract: coinContract, decimals } =
coinInfoList[swapMode === 'mint' ? selectedCoin : 'ousd']
let coinToReceiveDecimals, coinToReceiveContract
// do not enter conditional body when redeeming a mix
if (!(swapMode === 'redeem' && selectedCoin === 'mix')) {
;({ contract: coinToReceiveContract, decimals: coinToReceiveDecimals } =
coinInfoList[swapMode === 'redeem' ? selectedCoin : 'ousd'])
}
// plain amount as displayed in UI (not in wei format)
const amount = parseFloat(inputAmountRaw)
const { swapAmount, minSwapAmount } = calculateSwapAmounts(
inputAmountRaw,
decimals,
priceToleranceValue
)
useEffect(() => {
if (
!amount ||
!selectedSwap ||
!allowances ||
Object.keys(allowances) === 0
) {
return
}
const nameMaps = {
vault: 'vault',
flipper: 'flipper',
uniswap: 'uniswapV3Router',
uniswapV2: 'uniswapV2Router',
curve: 'curve',
sushiswap: 'sushiRouter',
}
const coinNeedingApproval = swapMode === 'mint' ? selectedCoin : 'ousd'
if (coinNeedingApproval === 'ousd' && selectedSwap.name === 'vault') {
setNeedsApproval(false)
} else {
if (nameMaps[selectedSwap.name] === undefined) {
throw new Error(
`Can not fetch contract: ${selectedSwap.name} allowance for coin: ${coinNeedingApproval}`
)
}
setNeedsApproval(
Object.keys(allowances).length > 0 &&
parseFloat(
allowances[coinNeedingApproval][nameMaps[selectedSwap.name]]
) < amount
? selectedSwap.name
: false
)
}
}, [swapMode, amount, allowances, selectedCoin, selectedSwap])
const _mintVault = async (
callObject,
swapAmount,
minSwapAmount,
txParams = {}
) => {
return await callObject.mint(
coinContract.address,
swapAmount,
minSwapAmount,
txParams
)
}
/* Increases the given gas limit by the specified buffer. BufferToIncrease is expressed
* in relative percentages. Meaning a 0.2 value will set gasLimit to 120% of the original value
*/
const increaseGasLimitByBuffer = (gasLimit, bufferToIncrease) => {
return Math.round(gasLimit * (1 + bufferToIncrease))
}
const mintVaultGasEstimate = async (swapAmount, minSwapAmount) => {
const gasEstimate = (
await _mintVault(vaultContract.estimateGas, swapAmount, minSwapAmount, {
from: account,
})
).toNumber()
return parseInt(
gasEstimate +
Math.max(
mintAbsoluteGasLimitBuffer,
gasEstimate * mintPercentGasLimitBuffer
)
)
}
const mintVault = async () => {
const { minSwapAmount: minSwapAmountReceived } = calculateSwapAmounts(
outputAmount,
18,
priceToleranceValue
)
const gasLimit = await mintVaultGasEstimate(
swapAmount,
minSwapAmountReceived
)
return {
result: await _mintVault(
connSigner(vaultContract),
swapAmount,
minSwapAmountReceived,
{
gasLimit,
}
),
swapAmount,
minSwapAmount: minSwapAmountReceived,
}
}
const _redeemVault = async (
callObject,
swapAmount,
minSwapAmount,
txParams = {}
) => {
const isRedeemAll = Math.abs(swapAmount - balances.ousd) < 1
if (isRedeemAll) {
return await callObject.redeemAll(minSwapAmount, txParams)
} else {
return await callObject.redeem(swapAmount, minSwapAmount, txParams)
}
}
const redeemVaultGasEstimate = async (swapAmount, minSwapAmount) => {
return increaseGasLimitByBuffer(
await _redeemVault(vaultContract.estimateGas, swapAmount, minSwapAmount, {
from: account,
}),
redeemPercentGasLimitBuffer
)
}
const redeemVault = async () => {
const { minSwapAmount: minSwapAmountReceived } = calculateSwapAmounts(
outputAmount,
18,
priceToleranceValue
)
const gasLimit = await redeemVaultGasEstimate(
swapAmount,
minSwapAmountReceived
)
return {
result: await _redeemVault(
connSigner(vaultContract),
swapAmount,
minSwapAmountReceived,
{
gasLimit,
}
),
swapAmount,
minSwapAmount: minSwapAmountReceived,
}
}
const swapFlipper = async () => {
// need to calculate these again, since Flipper takes all amount inputs in 18 decimal format
const { swapAmount: swapAmountFlipper } = calculateSwapAmounts(
inputAmountRaw,
18
)
let flipperResult
if (swapMode === 'mint') {
if (selectedCoin === 'dai') {
flipperResult = await connSigner(flipper).buyOusdWithDai(
swapAmountFlipper
)
} else if (selectedCoin === 'usdt') {
flipperResult = await connSigner(flipper).buyOusdWithUsdt(
swapAmountFlipper
)
} else if (selectedCoin === 'usdc') {
flipperResult = await connSigner(flipper).buyOusdWithUsdc(
swapAmountFlipper
)
}
} else {
if (selectedCoin === 'dai') {
flipperResult = await connSigner(flipper).sellOusdForDai(
swapAmountFlipper
)
} else if (selectedCoin === 'usdt') {
flipperResult = await connSigner(flipper).sellOusdForUsdt(
swapAmountFlipper
)
} else if (selectedCoin === 'usdc') {
flipperResult = await connSigner(flipper).sellOusdForUsdc(
swapAmountFlipper
)
}
}
return {
result: flipperResult,
swapAmount,
minSwapAmount,
}
}
/* Path is an array of strings -> contains all pool pairs enumerated
* Fees is an array of numbers -> identifying the pool fees of the pairs
*/
const _encodeUniswapPath = (path, fees) => {
const FEE_SIZE = 3
if (path.length != fees.length + 1) {
throw new Error('path/fee lengths do not match')
}
let encoded = '0x'
for (let i = 0; i < fees.length; i++) {
// 20 byte encoding of the address
encoded += path[i].slice(2)
// 3 byte encoding of the fee
encoded += fees[i].toString(16).padStart(2 * FEE_SIZE, '0')
}
// encode the final token
encoded += path[path.length - 1].slice(2)
return encoded.toLowerCase()
}
const _encodePath = () => {
const isMintMode = swapMode === 'mint'
let path
if (selectedCoin === 'dai') {
/* Uniswap now supports 0.01% fees on stablecoin pools
* TODO: can't get the 0.01% pool to work for DAI even though it is obviously available:
*
* - https://info.uniswap.org/#/pools/0x3416cf6c708da44db2624d63ea0aaef7113527c6 -> 0.01%
*/
if (isMintMode) {
path = _encodeUniswapPath(
[daiContract.address, usdtContract.address, ousdContract.address],
[500, 500]
)
} else {
path = _encodeUniswapPath(
[ousdContract.address, usdtContract.address, daiContract.address],
[500, 500]
)
}
} else if (selectedCoin === 'usdc') {
/* Uniswap now supports 0.01% fees on stablecoin pools
*
* - https://info.uniswap.org/#/pools/0x5777d92f208679db4b9778590fa3cab3ac9e2168 -> 0.01%
*/
if (isMintMode) {
path = _encodeUniswapPath(
[usdcContract.address, usdtContract.address, ousdContract.address],
[100, 500]
)
} else {
path = _encodeUniswapPath(
[ousdContract.address, usdtContract.address, usdcContract.address],
[500, 100]
)
}
} else {
throw new Error(
`Unexpected uniswap params -> swapMode: ${swapMode} selectedCoin: ${selectedCoin}`
)
}
return path
}
const _swapCurve = async (swapAmount, minSwapAmount, isGasEstimate) => {
const swapParams = [
curveMetapoolUnderlyingCoins.indexOf(coinContract.address.toLowerCase()),
curveMetapoolUnderlyingCoins.indexOf(
coinToReceiveContract.address.toLowerCase()
),
swapAmount,
minSwapAmount,
]
const gasLimit = increaseGasLimitByBuffer(
await curveOUSDMetaPool.estimateGas.exchange_underlying(...swapParams, {
from: account,
}),
curveGasLimitBuffer
)
if (isGasEstimate) {
return gasLimit
} else {
return await connSigner(curveOUSDMetaPool).exchange_underlying(
...swapParams,
{ gasLimit }
)
}
}
const swapCurveGasEstimate = async (swapAmount, minSwapAmount) => {
return await _swapCurve(swapAmount, minSwapAmount, true)
}
const swapCurve = async () => {
const { minSwapAmount: minSwapAmountReceived } = calculateSwapAmounts(
outputAmount,
coinToReceiveDecimals,
priceToleranceValue
)
return {
result: await _swapCurve(swapAmount, minSwapAmountReceived, false),
swapAmount,
minSwapAmount,
}
}
const quoteCurve = async (swapAmount) => {
const coinsReceived = await curveRegistryExchange.get_exchange_amount(
addresses.mainnet.CurveOUSDMetaPool,
coinContract.address,
coinToReceiveContract.address,
swapAmount,
{
gasLimit: 1000000,
}
)
return coinsReceived
}
const _swapUniswap = async (swapAmount, minSwapAmount, isGasEstimate) => {
const isMintMode = swapMode === 'mint'
const swapWithIncreaseGasLimit = async (
runEstimateFunction,
runSwapFunction
) => {
const gasLimit = await runEstimateFunction()
if (isGasEstimate) {
return gasLimit
} else {
return await runSwapFunction({ gasLimit })
}
}
if (selectedCoin === 'usdt') {
const singleCoinParams = [
isMintMode ? usdtContract.address : ousdContract.address,
isMintMode ? ousdContract.address : usdtContract.address,
500, // pre-defined Factory fee for stablecoins
account, // recipient
BigNumber.from(Date.now() + 2 * 60 * 1000), // deadline - 2 minutes from now
swapAmount, // amountIn
minSwapAmount, // amountOutMinimum
0, // sqrtPriceLimitX96
]
const runUsdtGasEstimate = async () => {
return increaseGasLimitByBuffer(
(
await uniV3SwapRouter.estimateGas.exactInputSingle(
singleCoinParams,
{ from: account }
)
).toNumber(),
uniswapV3GasLimitBuffer
)
}
return await swapWithIncreaseGasLimit(
runUsdtGasEstimate,
async (txParams) => {
return await connSigner(uniV3SwapRouter).exactInputSingle(
singleCoinParams,
txParams
)
}
)
}
const path = _encodePath()
const params = {
path,
recipient: account,
deadline: BigNumber.from(Date.now() + 2 * 60 * 1000), // deadline - 2 minutes from now,
amountIn: swapAmount,
amountOutMinimum: minSwapAmount,
}
const data = [
uniV3SwapRouter.interface.encodeFunctionData('exactInput', [params]),
]
const runGasEstimate = async () => {
return increaseGasLimitByBuffer(
(
await uniV3SwapRouter.estimateGas.exactInput(params, {
from: account,
})
).toNumber(),
uniswapV3GasLimitBuffer
)
}
return await swapWithIncreaseGasLimit(runGasEstimate, async (txParams) => {
return await connSigner(uniV3SwapRouter).exactInput(params, txParams)
})
}
const swapUniswapGasEstimate = async (swapAmount, minSwapAmount) => {
return await _swapUniswap(swapAmount, minSwapAmount, true)
}
const swapUniswap = async () => {
const { minSwapAmount: minSwapAmountReceived } = calculateSwapAmounts(
outputAmount,
coinToReceiveDecimals,
priceToleranceValue
)
return {
result: await _swapUniswap(swapAmount, minSwapAmountReceived, false),
swapAmount,
minSwapAmount,
}
}
const quoteUniswap = async (swapAmount) => {
const isMintMode = swapMode === 'mint'
if (selectedCoin === 'usdt') {
return await uniV3SwapQuoter.callStatic.quoteExactInputSingle(
isMintMode ? usdtContract.address : ousdContract.address,
isMintMode ? ousdContract.address : usdtContract.address,
500, // pre-defined Factory fee for stablecoins
swapAmount,
0 // sqrtPriceLimitX96
)
}
const path = _encodePath()
return await uniV3SwapQuoter.callStatic.quoteExactInput(path, swapAmount)
}
const _swapUniswapV2 = async (
swapAmount,
minSwapAmount,
isGasEstimate,
isSushiSwap = false
) => {
const isMintMode = swapMode === 'mint'
const contract = isSushiSwap ? sushiRouter : uniV2Router
const swapCallParams = [
swapAmount, // amountIn
minSwapAmount, // amountOutMinimum
getUniV2Path(), // swap path
account, // recipient
BigNumber.from(Date.now() + 2 * 60 * 1000), // deadline - 2 minutes from now
]
const txParams = { from: account }
const gasLimit = increaseGasLimitByBuffer(
await contract.estimateGas.swapExactTokensForTokens(
...swapCallParams,
txParams
),
isSushiSwap ? sushiswapGasLimitBuffer : uniswapV2GasLimitBuffer
)
if (isGasEstimate) {
return gasLimit
} else {
txParams.gasLimit = gasLimit
return await connSigner(contract).swapExactTokensForTokens(
...swapCallParams,
txParams
)
}
}
const swapUniswapV2GasEstimate = async (swapAmount, minSwapAmount) => {
return await _swapUniswapV2(swapAmount, minSwapAmount, true)
}
const _swapUniswapV2Variant = async (isSushiSwap = false) => {
const { minSwapAmount: minSwapAmountReceived } = calculateSwapAmounts(
outputAmount,
coinToReceiveDecimals,
priceToleranceValue
)
return {
result: await _swapUniswapV2(
swapAmount,
minSwapAmountReceived,
false,
isSushiSwap
),
swapAmount,
minSwapAmount,
}
}
const swapUniswapV2 = async () => {
return _swapUniswapV2Variant(false)
}
const swapSushiSwap = async () => {
return _swapUniswapV2Variant(true)
}
const swapSushiswapGasEstimate = async (swapAmount, minSwapAmount) => {
return await _swapUniswapV2(swapAmount, minSwapAmount, true, true)
}
const getUniV2Path = () => {
const isMintMode = swapMode === 'mint'
let path
if (selectedCoin === 'dai') {
if (isMintMode) {
path = [daiContract.address, usdtContract.address, ousdContract.address]
} else {
path = [ousdContract.address, usdtContract.address, daiContract.address]
}
} else if (selectedCoin === 'usdc') {
if (isMintMode) {
path = [
usdcContract.address,
usdtContract.address,
ousdContract.address,
]
} else {
path = [
ousdContract.address,
usdtContract.address,
usdcContract.address,
]
}
} else if (selectedCoin === 'usdt') {
if (isMintMode) {
path = [usdtContract.address, ousdContract.address]
} else {
path = [ousdContract.address, usdtContract.address]
}
} else {
throw new Error(
`Unexpected uniswap V2 params -> swapMode: ${swapMode} selectedCoin: ${selectedCoin}`
)
}
return path
}
const quoteSushiSwap = async (swapAmount) => {
return await sushiRouter.getAmountsOut(swapAmount, getUniV2Path())
}
const quoteUniswapV2 = async (swapAmount) => {
return await uniV2Router.getAmountsOut(swapAmount, getUniV2Path())
}
return {
allowancesLoaded,
needsApproval,
mintVault,
mintVaultGasEstimate,
redeemVault,
redeemVaultGasEstimate,
swapFlipper,
swapUniswapGasEstimate,
swapUniswap,
swapUniswapV2GasEstimate,
swapUniswapV2,
quoteUniswap,
quoteUniswapV2,
quoteSushiSwap,
swapSushiSwap,
swapSushiswapGasEstimate,
quoteCurve,
swapCurve,
swapCurveGasEstimate,
}
}
Example #17
Source File: SettingsDropdown.js From origin-dollar with MIT License | 4 votes |
SettingsDropdown = ({ setPriceToleranceValue, priceToleranceValue }) => {
const [settingsOpen, setSettingsOpen] = useState(false)
const [showWarning, setShowWarning] = useState()
const gasPrice = useStoreState(ContractStore, (s) => s.gasPrice)
useEffect(() => {
setShowWarning(priceToleranceValue > 1)
}, [priceToleranceValue])
return (
<div className="dropdown-holder">
<Dropdown
className="d-flex align-items-center min-h-42"
content={
<div className="d-flex flex-column dropdown-menu show">
<div className="d-flex justify-content-between align-items-center">
<div className="setting-title">
{fbt('Price tolerance', 'price tolerance setting')}
</div>
<div className="d-flex setting-holder">
<div className="w-50 d-flex align-items-center">
<input
value={priceToleranceValue}
className="tolerance h-100"
onChange={(e) => {
e.preventDefault()
let value = 0
if (!isNaN(e.target.value)) {
value = e.target.value
setShowWarning(value > 1)
value = value > 50 ? 50 : value
value = truncateDecimals(value, 2)
if (value !== priceToleranceValue) {
analytics.track('On price tolerance change', {
category: 'settings',
label: value,
})
}
setPriceToleranceValue(value)
}
}}
/>
<div>%</div>
</div>
<button
className="w-50 d-flex align-items-center justify-content-center auto"
onClick={() => {
setPriceToleranceValue(0.1)
setShowWarning(false)
}}
>
AUTO
</button>
</div>
</div>
<div className={`warning ${showWarning ? '' : 'hide'}`}>
Your transaction may be frontrun
</div>
<div className="d-flex justify-content-between align-items-center margin-top">
<div className="setting-title">
{fbt('Gas price', 'Gas price setting')}
</div>
<div className="d-flex setting-holder">
<div className="w-50">
<input
type="number"
value={Math.floor(gasPrice / Math.pow(10, 9))}
className="w-100 h-100"
onChange={(e) => {
let value = e.target.value
// ensure positive integers
if (value < 0) {
value = 0
}
value = Math.floor(value)
value *= Math.pow(10, 9)
analytics.track('On gas setting change', {
category: 'settings',
label: value,
})
ContractStore.update((s) => {
s.gasPrice = BigNumber.from(value)
s.isGasPriceUserOverriden = true
})
}}
/>
</div>
<div className="w-50 d-flex align-items-center justify-content-center gwei">
GWEI
</div>
</div>
</div>
</div>
}
open={settingsOpen}
onClose={() => setSettingsOpen(false)}
>
<img
className="settings-icon"
src={assetRootPath('/images/settings-icon.svg')}
onClick={(e) => {
const newOpenState = !settingsOpen
setSettingsOpen(newOpenState)
if (newOpenState) {
analytics.track('On open settings', {
category: 'settings',
})
}
}}
/>
</Dropdown>
<style jsx>{`
.dropdown-holder {
position: absolute;
top: 15px;
right: 15px;
}
.dropdown-menu {
top: 115%;
left: 0;
right: auto;
min-width: 290px;
padding: 18px 18px 18px 20px;
}
.settings-icon {
width: 18px;
height: 18px;
cursor: pointer;
}
.setting-title {
font-size: 14px;
font-weight: bold;
color: #8293a4;
}
.margin-top {
margin-top: 15px;
}
.tolerance {
width: 70%;
}
.setting-holder {
max-width: 120px;
min-width: 120px;
max-height: 40px;
min-height: 40px;
border-radius: 5px;
border: solid 1px #cdd7e0;
background-color: #f2f3f5;
}
input {
max-width: 60px;
border: 0px;
font-size: 14px;
font-weight: normal;
color: black;
text-align: center;
border-radius: 5px 0 0 5px;
background-color: #f2f3f5;
}
.warning {
font-size: 14px;
color: #ff8000;
margin-top: 10px;
}
.warning.hide {
display: none;
}
.gwei {
font-size: 14px;
color: #8293a4;
background-color: white;
border-radius: 0 5px 5px 0;
border-left: solid 1px #cdd7e0;
}
button.auto {
font-size: 14px;
color: white;
background-color: #1a82ff;
border-radius: 0 5px 5px 0;
border: 0;
border-left: solid 1px #cdd7e0;
}
@media (max-width: 799px) {
.dropdown-holder {
top: 7px;
right: 7px;
}
.dropdown-menu {
top: 115%;
right: 0;
left: auto;
padding: 12px 12px 12px 14px;
}
}
`}</style>
</div>
)
}
Example #18
Source File: AccountListener.js From origin-dollar with MIT License | 4 votes |
AccountListener = (props) => {
const web3react = useWeb3React()
const { account, chainId, library, active } = web3react
const prevAccount = usePrevious(account)
const prevActive = usePrevious(active)
const [contracts, setContracts] = useState(null)
const [cookies, setCookie, removeCookie] = useCookies(['loggedIn'])
const {
active: userActive,
refetchUserData,
refetchStakingData,
} = useStoreState(AccountStore, (s) => s)
const durations = useStoreState(StakeStore, (s) => s.durations)
const rates = useStoreState(StakeStore, (s) => s.rates)
const prevRefetchStakingData = usePrevious(refetchStakingData)
const prevRefetchUserData = usePrevious(refetchUserData)
const AIR_DROPPED_STAKE_TYPE = 1
const balancesQuery = useBalancesQuery(account, contracts, {
onSuccess: (balances) => {
AccountStore.update((s) => {
s.balances = balances
})
},
})
const allowancesQuery = useAllowancesQuery(account, contracts, {
onSuccess: (allowances) => {
AccountStore.update((s) => {
s.allowances = allowances
})
},
})
const wousdQuery = useWousdQuery(account, contracts, {
onSuccess: (wousdValue) => {
AccountStore.update((s) => {
s.wousdValue = wousdValue
})
},
})
const apyQuery = useApyQuery({
onSuccess: (apy) => {
ContractStore.update((s) => {
s.apy = apy
})
},
})
const historyQuery = useTransactionHistoryQuery(account)
useEffect(() => {
if ((prevActive && !active) || prevAccount !== account) {
AccountStore.update((s) => {
s.allowances = {}
s.balances = {}
})
PoolStore.update((s) => {
s.claimable_ogn = null
s.lp_tokens = null
s.lp_token_allowance = null
s.staked_lp_tokens = null
s.your_weekly_rate = null
})
StakeStore.update((s) => {
s.stakes = null
s.airDropStakeClaimed = false
})
}
}, [active, prevActive, account, prevAccount])
useEffect(() => {
const fetchVaultThresholds = async () => {
if (!contracts) return
const vault = contracts.vault
const allocateThreshold = await vault.autoAllocateThreshold()
const rebaseThreshold = await vault.rebaseThreshold()
ContractStore.update((s) => {
s.vaultAllocateThreshold = allocateThreshold
s.vaultRebaseThreshold = rebaseThreshold
})
}
fetchVaultThresholds()
}, [contracts])
const loadData = async (contracts, { onlyStaking } = {}) => {
if (!account) {
return
}
if (!contracts.ogn.provider) {
console.warn('Contract provider not yet set')
return
}
if (!contracts) {
console.warn('Contracts not yet loaded!')
return
}
if (!isCorrectNetwork(chainId)) {
return
}
const { ousd, ogn, ognStakingView } = contracts
const loadPoolRelatedAccountData = async () => {
if (!account) return
if (process.env.ENABLE_LIQUIDITY_MINING !== 'true') return
const pools = PoolStore.currentState.pools
const initializedPools = pools.filter((pool) => pool.contract)
if (pools.length !== initializedPools.length) {
console.warn(
'When loading account pool data not all pools have yet initialized'
)
}
// contract needs to be populated?
// await poolContract.userInfo(account)
// await displayCurrency(userInfo.amount, lpContract)
try {
const additionalPoolData = await Promise.all(
pools.map(async (pool) => {
const { lpContract, contract, pool_deposits_bn } = pool
const additionalData = {
name: pool.name,
coin_one: {},
coin_two: {},
}
if (isDevelopment) {
const token1Contract =
contracts[pool.coin_one.contract_variable_name]
const token2Contract =
contracts[pool.coin_two.contract_variable_name]
const [
coin1Allowance,
coin2Allowance,
coin1Balance,
coin2Balance,
] = await Promise.all([
displayCurrency(
await token1Contract.allowance(account, lpContract.address),
token1Contract
),
displayCurrency(
await token2Contract.allowance(account, lpContract.address),
token2Contract
),
displayCurrency(
await token1Contract.balanceOf(account),
token1Contract
),
displayCurrency(
await token2Contract.balanceOf(account),
token2Contract
),
])
additionalData.coin_one.allowance = coin1Allowance
additionalData.coin_two.allowance = coin2Allowance
additionalData.coin_one.balance = coin1Balance
additionalData.coin_two.balance = coin2Balance
additionalData.coin_one.contract = token1Contract
additionalData.coin_two.contract = token2Contract
}
const [
userInfo,
unclaimedOgn,
lp_tokens,
lp_token_allowance,
rewardPerBlockBn,
userReward,
poolDepositsBn,
] = await Promise.all([
await contract.userInfo(account),
displayCurrency(await contract.pendingRewards(account), ogn),
displayCurrency(await lpContract.balanceOf(account), lpContract),
displayCurrency(
await lpContract.allowance(account, contract.address),
lpContract
),
await contract.rewardPerBlock(),
displayCurrency(await contract.pendingRewards(account), ogn),
await lpContract.balanceOf(contract.address),
])
const userTokensStaked = await displayCurrency(
userInfo.amount,
lpContract
)
additionalData.claimable_ogn = unclaimedOgn
additionalData.lp_tokens = lp_tokens
additionalData.lp_token_allowance = lp_token_allowance
additionalData.staked_lp_tokens = userTokensStaked
additionalData.pool_deposits = await displayCurrency(
poolDepositsBn,
lpContract
)
additionalData.reward_per_block = await displayCurrency(
rewardPerBlockBn,
ogn
)
const userTokensStakedNumber = Number(userTokensStaked)
/* userTokensStaked / total pool deposits = the share of the pool a user owns
* Multiplied by rewards per block times number of blocks in a week
*/
additionalData.your_weekly_rate =
userTokensStakedNumber === 0 || poolDepositsBn.isZero()
? 0
: await displayCurrency(
userInfo.amount
/* in dev environment sometimes users can have more tokens staked than total pool token staked.
* that happens when user balance updates before the pool balance.
*/
.div(poolDepositsBn)
.mul(rewardPerBlockBn)
.mul(BigNumber.from(6500 * 7)), // blocks in a day times 7 days in a week
ogn
)
return additionalData
})
)
const enrichedPools = PoolStore.currentState.pools.map((pool) => {
const additionalData = additionalPoolData.filter(
(apool) => apool.name === pool.name
)[0]
const merged = {
...pool,
...additionalData,
coin_one: {
...pool.coin_one,
...additionalData.coin_one,
},
coin_two: {
...pool.coin_two,
...additionalData.coin_two,
},
}
return merged
})
//console.log('Enriched pools', enrichedPools)
PoolStore.update((s) => {
s.pools = enrichedPools
})
} catch (e) {
console.error(
'AccountListener.js error - can not load account specific data for pools',
e
)
}
}
const loadStakingRelatedData = async () => {
if (!account) return
try {
/* OgnStakingView is used here instead of ognStaking because the first uses the jsonRpcProvider and
* the latter the wallet one. Sometime these are not completely in sync and while the first one might
* report a transaction already mined, the second one not yet.
*
* We use jsonRpcProvider to wait for transactions to be mined, so using the samne provider to fetch the
* staking data solves the out of sync problem.
*/
const [stakes, airDropStakeClaimed] = await Promise.all([
ognStakingView.getAllStakes(account),
ognStakingView.airDroppedStakeClaimed(
account,
AIR_DROPPED_STAKE_TYPE
),
])
const decoratedStakes = stakes
? decorateContractStakeInfoWithTxHashes(stakes)
: []
StakeStore.update((s) => {
s.stakes = decoratedStakes
s.airDropStakeClaimed = airDropStakeClaimed
})
} catch (e) {
console.error(
'AccountListener.js error - can not load staking related data: ',
e
)
}
}
const loadRebaseStatus = async () => {
if (!account) return
// TODO handle other contract types. We only detect Gnosis Safe as having
// opted out here as rebaseState will always be 0 for all EOAs
const isSafe = !!_.get(library, 'provider.safe.safeAddress', false)
AccountStore.update((s) => {
s.isSafe = isSafe
})
const rebaseOptInState = await ousd.rebaseState(account)
AccountStore.update((s) => {
s.rebaseOptedOut = isSafe && rebaseOptInState === 0
})
}
if (onlyStaking) {
await loadStakingRelatedData()
} else {
balancesQuery.refetch()
allowancesQuery.refetch()
wousdQuery.refetch()
await Promise.all([
loadRebaseStatus(),
// TODO maybe do this if only in the LM part of the dapp since it is very heavy
loadPoolRelatedAccountData(),
loadStakingRelatedData(),
])
}
}
useEffect(() => {
if (account) {
login(account, setCookie)
historyQuery.refetch()
}
const loadLifetimeEarnings = async () => {
if (!account) return
const response = await fetch(
`${
process.env.ANALYTICS_ENDPOINT
}/api/v1/address/${account.toLowerCase()}/yield`
)
if (response !== undefined && response.ok) {
const lifetimeYield = (await response.json()).lifetime_yield
AccountStore.update((s) => {
s.lifetimeYield = lifetimeYield
})
}
}
const setupContractsAndLoad = async () => {
/* If we have a web3 provider present and is signed into the allowed network:
* - NODE_ENV === production -> mainnet
* - NODE_ENV === development -> localhost, forknet
* then we use that chainId to setup contracts.
*
* In other case we still want to have read only capability of the contracts with a general provider
* so we can fetch `getAPR` from Vault for example to use on marketing pages even when the user is not
* logged in with a web3 provider.
*
*/
let usedChainId, usedLibrary
if (chainId && isCorrectNetwork(chainId)) {
usedChainId = chainId
usedLibrary = library
} else {
usedChainId = parseInt(process.env.ETHEREUM_RPC_CHAIN_ID)
usedLibrary = null
}
window.fetchId = window.fetchId ? window.fetchId : 0
window.fetchId += 1
apyQuery.refetch()
const contracts = await setupContracts(
account,
usedLibrary,
usedChainId,
window.fetchId
)
setContracts(contracts)
setTimeout(() => {
loadData(contracts)
}, 1)
}
setupContractsAndLoad()
loadLifetimeEarnings()
}, [account, chainId])
useEffect(() => {
// trigger a force refetch user data when the flag is set by a user
if (
(contracts && isCorrectNetwork(chainId),
refetchUserData && !prevRefetchUserData)
) {
loadData(contracts)
}
AccountStore.update((s) => {
s.refetchUserData = false
})
}, [userActive, contracts, refetchUserData, prevRefetchUserData])
useEffect(() => {
// trigger a force refetch user data when the flag is set by a user
if (
(contracts && isCorrectNetwork(chainId),
refetchStakingData && !prevRefetchStakingData)
) {
loadData(contracts, { onlyStaking: true })
}
AccountStore.update((s) => {
s.refetchStakingData = false
})
}, [userActive, contracts, refetchStakingData, prevRefetchStakingData])
useEffect(() => {
let balancesInterval
if (contracts && userActive === 'active' && isCorrectNetwork(chainId)) {
loadData(contracts)
balancesInterval = setInterval(() => {
loadData(contracts)
}, 7000)
}
return () => {
if (balancesInterval) {
clearInterval(balancesInterval)
}
}
}, [userActive, contracts])
return ''
}
Example #19
Source File: dashboard.js From origin-dollar with MIT License | 4 votes |
Dashboard = ({ locale, onLocale }) => {
const allowances = useStoreState(AccountStore, (s) => s.allowances)
const balances = useStoreState(AccountStore, (s) => s.balances)
const pools = useStoreState(PoolStore, (s) => s.pools)
const account = useStoreState(AccountStore, (s) => s.address)
const { chainId } = useWeb3React()
const {
vault,
usdt,
dai,
tusd,
usdc,
ousd,
viewVault,
ogn,
uniV2OusdUsdt,
liquidityOusdUsdt,
ognStaking,
compensation,
uniV3OusdUsdt,
uniV3DaiUsdt,
uniV3UsdcUsdt,
uniV3NonfungiblePositionManager,
uniV3SwapRouter,
flipper,
} = useStoreState(ContractStore, (s) => s.contracts || {})
const isMainnetFork = process.env.NODE_ENV === 'development' && chainId === 1
const isGovernor = account && account === governorAddress
const [refreshFlipperData, setRefreshFlipperData] = useState(0)
const [refreshUniV3Data, setRefreshUniV3Data] = useState(0)
const [flipperData, setFlipperData] = useState({})
const [uniV3Data, setUniV3Data] = useState({})
const [adjusterLocked, setAdjusterLocked] = useState(null)
const [compensationTotalClaims, setCompensationTotalClaims] =
useState('Loading...')
const updateAdjuster = async () => {
setAdjusterLocked(await compensation.isAdjusterLocked())
}
const loadTotalClaims = async () => {
setCompensationTotalClaims(
await displayCurrency(await compensation.totalClaims(), ousd)
)
}
useEffect(() => {
if (compensation && compensation.provider) {
updateAdjuster()
loadTotalClaims()
}
}, [compensation])
useEffect(() => {
const refreshDataInterval = setInterval(() => {
setRefreshFlipperData(refreshFlipperData + Math.random())
setRefreshUniV3Data(refreshUniV3Data + Math.random())
}, 4000)
return () => {
clearInterval(refreshDataInterval)
}
}, [])
useEffect(() => {
if (
!(
!dai ||
!dai.provider ||
!usdc ||
!usdc.provider ||
!usdt ||
!usdt.provider ||
!ousd ||
!ousd.provider
)
) {
const refreshBalances = async () => {
const daiAmount = await dai.balanceOf(flipper.address)
const usdtAmount = await usdt.balanceOf(flipper.address)
const usdcAmount = await usdc.balanceOf(flipper.address)
const ousdAmount = await ousd.balanceOf(flipper.address)
const daiAllowance = await displayCurrency(
await dai.allowance(account, flipper.address),
dai
)
const usdtAllowance = await displayCurrency(
await usdt.allowance(account, flipper.address),
usdt
)
const usdcAllowance = await displayCurrency(
await usdc.allowance(account, flipper.address),
usdc
)
const ousdAllowance = await displayCurrency(
await ousd.allowance(account, flipper.address),
ousd
)
setFlipperData({
daiBalance: daiAmount,
usdtBalance: usdtAmount,
usdcBalance: usdcAmount,
ousdBalance: ousdAmount,
daiAllowance: daiAllowance,
usdtAllowance: usdtAllowance,
usdcAllowance: usdcAllowance,
ousdAllowance: ousdAllowance,
})
}
refreshBalances()
}
}, [refreshFlipperData, dai, usdc, usdt, ousd])
useEffect(() => {
if (
!(
!usdt ||
!usdt.provider ||
!ousd ||
!ousd.provider ||
!uniV3SwapRouter ||
!uniV3SwapRouter.provider
)
) {
let usdtAllowanceManager,
ousdAllowanceManager,
daiAllowanceManager,
usdcAllowanceManager = 'Loading'
const refreshUniswapData = async () => {
const usdtAllowanceRouter = await displayCurrency(
await usdt.allowance(account, uniV3SwapRouter.address),
usdt
)
const ousdAllowanceRouter = await displayCurrency(
await ousd.allowance(account, uniV3SwapRouter.address),
ousd
)
const daiAllowanceRouter = await displayCurrency(
await dai.allowance(account, uniV3SwapRouter.address),
usdt
)
const usdcAllowanceRouter = await displayCurrency(
await usdc.allowance(account, uniV3SwapRouter.address),
ousd
)
if (!isProduction) {
usdtAllowanceManager = await displayCurrency(
await usdt.allowance(
account,
uniV3NonfungiblePositionManager.address
),
usdt
)
ousdAllowanceManager = await displayCurrency(
await ousd.allowance(
account,
uniV3NonfungiblePositionManager.address
),
ousd
)
daiAllowanceManager = await displayCurrency(
await dai.allowance(
account,
uniV3NonfungiblePositionManager.address
),
dai
)
usdcAllowanceManager = await displayCurrency(
await usdc.allowance(
account,
uniV3NonfungiblePositionManager.address
),
usdc
)
}
const usdtBalancePoolousd_usdt = await displayCurrency(
await usdt.balanceOf(uniV3OusdUsdt.address),
usdt
)
const ousdBalancePoolousd_usdt = await displayCurrency(
await ousd.balanceOf(uniV3OusdUsdt.address),
ousd
)
const usdtBalancePooldai_usdt = await displayCurrency(
await usdt.balanceOf(uniV3DaiUsdt.address),
usdt
)
const daiBalancePooldai_usdt = await displayCurrency(
await dai.balanceOf(uniV3DaiUsdt.address),
dai
)
const usdtBalancePoolusdc_usdt = await displayCurrency(
await usdt.balanceOf(uniV3UsdcUsdt.address),
usdt
)
const usdcBalancePoolusdc_usdt = await displayCurrency(
await usdc.balanceOf(uniV3UsdcUsdt.address),
usdc
)
setUniV3Data({
usdtAllowanceRouter,
ousdAllowanceRouter,
daiAllowanceRouter,
usdcAllowanceRouter,
usdtAllowanceManager,
ousdAllowanceManager,
daiAllowanceManager,
usdcAllowanceManager,
usdtBalancePoolousd_usdt,
ousdBalancePoolousd_usdt,
usdtBalancePooldai_usdt,
daiBalancePooldai_usdt,
usdtBalancePoolusdc_usdt,
usdcBalancePoolusdc_usdt,
})
}
refreshUniswapData()
}
}, [
refreshUniV3Data,
usdt,
ousd,
uniV3SwapRouter,
uniV3NonfungiblePositionManager,
])
const randomAmount = (multiple = 0) => {
return String(
Math.floor(Math.random() * (999999999 * multiple)) / 100000 + 1000
)
}
const mintByCommandLineOption = () => {
if (isMainnetFork) {
alert(
"To grant stable coins go to project's 'contracts' folder and run 'yarn run grant-stable-coins:fork' "
)
}
}
const notSupportedOption = () => {
if (isMainnetFork) {
alert("Not supported when running main net fork -> 'yarn run node:fork'")
}
}
const clearAllAllowances = async () => {
notSupportedOption()
await usdt.decreaseAllowance(
vault.address,
ethers.utils.parseUnits(allowances['usdt'].vault, await usdt.decimals())
)
await dai.decreaseAllowance(
vault.address,
ethers.utils.parseUnits(allowances['dai'].vault, await dai.decimals())
)
await usdc.decreaseAllowance(
vault.address,
ethers.utils.parseUnits(allowances['usdc'].vault, await usdc.decimals())
)
await ousd.decreaseAllowance(
vault.address,
ethers.utils.parseUnits(allowances['ousd'].vault, await ousd.decimals())
)
}
const sendOUSDToContract = async () => {
await ousd.transfer(
compensation.address,
ethers.utils.parseUnits('20000000', await ousd.decimals())
)
}
const startClaimPeriod = async (seconds) => {
await compensation.start(seconds)
}
const setAdjusterLock = async (lock) => {
if (lock) {
await compensation.lockAdjuster()
} else {
await compensation.unlockAdjuster()
}
await updateAdjuster()
}
const mintUSDT = async (multiple) => {
mintByCommandLineOption()
await usdt.mint(
ethers.utils.parseUnits(randomAmount(multiple), await usdt.decimals())
)
}
const sendCoinToFlipper = async (coinContract, amount) => {
await coinContract.transfer(
flipper.address,
ethers.utils.parseUnits(amount.toString(), await coinContract.decimals())
)
setRefreshFlipperData(refreshFlipperData + 1)
}
const approveFlipper = async (coinContract) => {
await coinContract.approve(flipper.address, ethers.constants.MaxUint256)
}
const swapFlipperUsdtToOusd = async (bnAmount) => {
await flipper.buyOusdWithUsdt(bnAmount)
}
const mintOGN = async (multiple) => {
mintByCommandLineOption()
await ogn.mint(
ethers.utils.parseUnits(randomAmount(multiple), await ogn.decimals())
)
}
const sendOGNToStakingContract = async () => {
await ogn.transfer(
ognStaking.address,
ethers.utils.parseUnits('1000000', await ogn.decimals())
)
}
const approveStakingToMoveOgn = async () => {
notSupportedOption()
await ogn.approve(ognStaking.address, ethers.constants.MaxUint256)
}
const approveUSDT = async () => {
notSupportedOption()
await usdt.approve(vault.address, ethers.constants.MaxUint256)
}
const mintDAI = async (multiple) => {
mintByCommandLineOption()
await dai.mint(
ethers.utils.parseUnits(randomAmount(multiple), await dai.decimals())
)
}
const approveDAI = async () => {
notSupportedOption()
await dai.approve(vault.address, ethers.constants.MaxUint256)
}
const mintUSDC = async (multiple) => {
mintByCommandLineOption()
await usdc.mint(
ethers.utils.parseUnits(randomAmount(multiple), await usdc.decimals())
)
}
const approveUSDC = async () => {
notSupportedOption()
await usdc.approve(vault.address, ethers.constants.MaxUint256)
}
// const mintTUSD = async (amount) => {
// mintByCommandLineOption()
// await tusd.mint(
// ethers.utils.parseUnits(amount || randomAmount(), await tusd.decimals())
// )
// }
// const approveTUSD = async () => {
// notSupportedOption()
// await tusd.approve(
// vault.address,
// ethers.constants.MaxUint256
// )
// }
const buyOUSD = async () => {
await ousd.mint(
usdt.address,
ethers.utils.parseUnits('100.0', await usdt.decimals())
)
}
const depositYield = async () => {
notSupportedOption()
await ousd.depositYield(
usdt.address,
ethers.utils.parseUnits('10.0', await usdt.decimals())
)
}
const unPauseDeposits = async () => {
notSupportedOption()
await vault.unpauseDeposits()
}
const approveOUSD = async () => {
notSupportedOption()
await ousd.approve(vault.address, ethers.constants.MaxUint256)
}
const redeemOutputs = async () => {
const result = await vault.calculateRedeemOutputs(
ethers.utils.parseUnits('10', await ousd.decimals())
)
console.log(result)
}
const redeemDAI = async () => {
await vault.redeemAll(dai.address)
}
const redeemUSDT = async () => {
await vault.redeemAll(usdt.address)
}
const redeemUSDC = async () => {
await vault.redeemAll(usdc.address)
}
const setRedeemFee = async (amount) => {
await vault.setRedeemFeeBps(ethers.utils.parseUnits(amount.toString(), 0))
}
const approveUSDTForUniswapOUSD_USDT = async () => {
notSupportedOption()
await usdt.approve(uniV2OusdUsdt.address, ethers.constants.MaxUint256)
}
const approveOUSDForUniswapOUSD_USDT = async () => {
notSupportedOption()
await ousd.approve(uniV2OusdUsdt.address, ethers.constants.MaxUint256)
}
const approveForUniswapV3Router = async (coinContract) => {
notSupportedOption()
await coinContract.approve(
uniV3SwapRouter.address,
ethers.constants.MaxUint256
)
setRefreshUniV3Data(refreshUniV3Data + 1)
}
const approveForUniswapV3Manager = async (coinContract) => {
notSupportedOption()
await coinContract.approve(
uniV3NonfungiblePositionManager.address,
ethers.constants.MaxUint256
)
setRefreshUniV3Data(refreshUniV3Data + 1)
}
const initializeUniswapV3 = async (contract1, contract2, poolContract) => {
const sqrtPriceX96 = encodePriceSqrt(
ethers.utils.parseUnits('1', await contract1.decimals()),
ethers.utils.parseUnits('1', await contract2.decimals())
)
// TODO: This isn't initialized correctly so prices are off, but the transactions still execute
// console.log('PRICE: ', sqrtPriceX96.toString())
// the sqrtPriceX96 taken directly from pool creation on mainnet: https://etherscan.io/tx/0xe83eb25244b0e3a5b040f824ac9983cff0bc610747df45bf57755ef7b4bc3c74
// await uniV3OusdUsdt.initialize(BigNumber.from('79224306130848112672356'))
await poolContract.initialize(sqrtPriceX96)
}
const provideLiquidityV3 = async (contract1, contract2) => {
// Below part done directly by this periphery contract:
// https://github.com/Uniswap/uniswap-v3-periphery/blob/9ca9575d09b0b8d985cc4d9a0f689f7a4470ecb7/contracts/base/LiquidityManagement.sol#L80-L86
// If error 'LOK' is thrown then the pool might have not been initialized
const result = await uniV3NonfungiblePositionManager.mint([
contract1.address,
contract2.address,
500, // pre-defined Factory fee for stablecoins
20, // tick lower
50, // tick upper
ethers.utils.parseUnits('1000', await contract1.decimals()), // amount0Desired
ethers.utils.parseUnits('1000', await contract2.decimals()), // amount1Desired
//ethers.utils.parseUnits('900', 18), // amount0Min
0,
0,
account, // recipient
BigNumber.from(Date.now() + 10000), // deadline - 10 seconds from now
])
}
const testUniV3Swap100Coin1to2 = async (contract1, contract2) => {
// If error 'LOK' is thrown then the pool might have not been initialized
await uniV3SwapRouter.exactInputSingle([
contract1.address,
contract2.address,
500, // pre-defined Factory fee for stablecoins
account, // recipient
BigNumber.from(Date.now() + 10000), // deadline - 10 seconds from now
ethers.utils.parseUnits('100', await contract1.decimals()), // amountIn
//ethers.utils.parseUnits('98', await usdt.decimals()), // amountOutMinimum
0, // amountOutMinimum
0, // sqrtPriceLimitX96
])
}
const testUniV3Swap100Coin2to1 = async (contract1, contract2) => {
// If error 'LOK' is thrown then the pool might have not been initialized
await uniV3SwapRouter.exactInputSingle([
contract2.address,
contract1.address,
500, // pre-defined Factory fee for stablecoins
account, // recipient
BigNumber.from(Date.now() + 2 * 60 * 1000), // deadline - 2 minutes from now
ethers.utils.parseUnits('100', await contract2.decimals()), // amountIn
//ethers.utils.parseUnits('98', await usdt.decimals()), // amountOutMinimum
0, // amountOutMinimum
0, // sqrtPriceLimitX96
])
}
const setupSupportAssets = async () => {
notSupportedOption()
await vault.supportAsset(dai.address, 'DAI')
await vault.supportAsset(usdt.address, 'USDT')
await vault.supportAsset(usdc.address, 'USDC')
}
const tableRows = () => {
return [...Object.keys(currencies), 'ousd'].map((x) => {
const name = x.toUpperCase()
const balance = get(balances, x)
const allowance = Number(get(allowances, `${x}.vault`))
const unlimited = allowance && allowance > Number.MAX_SAFE_INTEGER
return (
<tr key={x}>
<td>{name}</td>
<td>{unlimited ? 'Unlimited' : allowance ? 'Some' : 'None'}</td>
<td>1</td>
<td>{formatCurrency(balance)}</td>
<td>{unlimited ? 'Max' : formatCurrency(allowance)}</td>
</tr>
)
})
}
const displayPoolInfo = (
coin1,
coin2,
coin1Contract,
coin2Contract,
poolContract
) => {
const poolName = coin1.toUpperCase() + '/' + coin2.toUpperCase()
return (
<div>
<h3>{poolName}</h3>
<table className="table table-bordered">
<thead>
<tr>
<td>Asset</td>
<td>Router - Allowance</td>
<td>Liquidity Manger - Allowance </td>
<td>{poolName} Pool - Balance</td>
</tr>
</thead>
<tbody>
{[coin1, coin2].map((coin) => {
const name = coin.toUpperCase()
const coinToDecimals = {
usdt: 6,
usdc: 6,
dai: 18,
ousd: 18,
}
const allowanceRouter = uniV3Data[`${coin}AllowanceRouter`]
const allowanceManager = uniV3Data[`${coin}AllowanceManager`]
const poolBalance =
uniV3Data[`${coin}BalancePool${coin1}_${coin2}`]
return (
<tr key={name}>
<td>{name}</td>
<td>
{allowanceRouter
? allowanceRouter === '0.0'
? allowanceRouter
: 'Max'
: 'Loading'}
</td>
<td>
{allowanceManager
? allowanceManager === '0.0'
? allowanceManager
: 'Max'
: 'Loading'}
</td>
<td>{poolBalance}</td>
</tr>
)
})}
</tbody>
</table>
<div className="d-flex flex-wrap">
<div
className="btn btn-primary my-4 mr-3"
onClick={() =>
initializeUniswapV3(coin1Contract, coin2Contract, poolContract)
}
>
Initialize Pool
</div>
<div
className="btn btn-primary my-4 mr-3"
onClick={() => provideLiquidityV3(coin1Contract, coin2Contract)}
>
Provide Liquidity
</div>
<div
className="btn btn-primary my-4 mr-3"
onClick={() =>
testUniV3Swap100Coin1to2(coin1Contract, coin2Contract)
}
>
Test Uniswap 100 {coin1}
</div>
<div
className="btn btn-primary my-4 mr-3"
onClick={() =>
testUniV3Swap100Coin2to1(coin1Contract, coin2Contract)
}
>
Test Uniswap 100 {coin2}
</div>
</div>
</div>
)
}
return (
<>
<Layout locale={locale} onLocale={onLocale} dapp>
<Nav dapp locale={locale} onLocale={onLocale} />
<div className="my-5">
{!account && <h1 className="text-white">No account :(</h1>}
{account && (
<>
<h1>Balances</h1>
<div className="card w25 mb-4">
<div className="card-body">
<h5 className="card-title">Current Balance</h5>
<p className="card-text">
{formatCurrency(get(balances, 'ousd'))} OUSD
</p>
</div>
</div>
<table className="table table-bordered">
<thead>
<tr>
<td>Asset</td>
<td>Permission</td>
<td>Exchange Rate</td>
<td>Your Balance</td>
<td>Allowance</td>
</tr>
</thead>
<tbody>{tableRows()}</tbody>
</table>
<div className="d-flex flex-wrap">
<div
className="btn btn-primary my-4 mr-3"
onClick={() => mintUSDT()}
>
Mint 1,000 USDT
</div>
<div
className="btn btn-primary my-4 mr-3"
onClick={() => mintUSDT(1)}
>
Mint random USDT
</div>
<div
className="btn btn-primary my-4 mr-3"
onClick={() => mintUSDT(10000)}
>
Mint hella USDT
</div>
<div
className="btn btn-primary my-4 mr-3"
onClick={approveUSDT}
>
Approve USDT
</div>
<div className="btn btn-primary my-4 mr-3" onClick={redeemUSDT}>
Redeem USDT
</div>
</div>
<div className="d-flex flex-wrap">
<div
className="btn btn-primary my-4 mr-3"
onClick={() => mintDAI()}
>
Mint 1,000 DAI
</div>
<div
className="btn btn-primary my-4 mr-3"
onClick={() => mintDAI(1)}
>
Mint random DAI
</div>
<div
className="btn btn-primary my-4 mr-3"
onClick={() => mintDAI(10000)}
>
Mint hella DAI
</div>
<div className="btn btn-primary my-4 mr-3" onClick={approveDAI}>
Approve DAI
</div>
<div className="btn btn-primary my-4 mr-3" onClick={redeemDAI}>
Redeem DAI
</div>
</div>
<div className="d-flex flex-wrap">
<div
className="btn btn-primary my-4 mr-3"
onClick={() => mintUSDC()}
>
Mint 1,000 USDC
</div>
<div
className="btn btn-primary my-4 mr-3"
onClick={() => mintUSDC(1)}
>
Mint random USDC
</div>
<div
className="btn btn-primary my-4 mr-3"
onClick={() => mintUSDC(10000)}
>
Mint hella USDC
</div>
<div
className="btn btn-primary my-4 mr-3"
onClick={approveUSDC}
>
Approve USDC
</div>
<div className="btn btn-primary my-4 mr-3" onClick={redeemUSDC}>
Redeem USDC
</div>
</div>
{/*
<div className="d-flex flex-wrap">
<div className="btn btn-primary my-4 mr-3" onClick={() => mintTUSD()}>
Mint TUSD
</div>
<div className="btn btn-primary my-4 mr-3" onClick={approveTUSD}>
Approve TUSD
</div>
</div>
*/}
<div className="d-flex flex-wrap">
{isGovernor && (
<div
className="btn btn-primary my-4 mr-3"
onClick={depositYield}
>
Deposit $10 Yield
</div>
)}
<div
className="btn btn-primary my-4 mr-3"
onClick={clearAllAllowances}
>
Clear All Allowances
</div>
<div className="btn btn-primary my-4 mr-3" onClick={buyOUSD}>
Buy OUSD
</div>
<div
className="btn btn-primary my-4 mr-3"
onClick={unPauseDeposits}
>
Un-Pause Deposits
</div>
<div
className="btn btn-primary my-4 mr-3"
onClick={approveOUSD}
>
Approve OUSD
</div>
<div
className="btn btn-primary my-4 mr-3"
onClick={setupSupportAssets}
>
Support DAI & USDT & USDC
</div>
<div
className="btn btn-primary my-4 mr-3"
onClick={redeemOutputs}
>
Calculate Redeem outputs
</div>
</div>
<h1 className="mt-5">Staking</h1>
<table className="table table-bordered">
<thead>
<tr>
<td>OGN balance</td>
<td>{formatCurrency(get(balances, 'ogn'))}</td>
</tr>
</thead>
</table>
<div className="d-flex flex-wrap">
<div
className="btn btn-primary my-4 mr-3"
onClick={() => mintOGN(10000)}
>
Mint hella OGN
</div>
<div
className="btn btn-primary my-4 mr-3"
onClick={() => sendOGNToStakingContract()}
>
Supply staking contract with OGN
</div>
<div
className="btn btn-primary my-4 mr-3"
onClick={() => approveStakingToMoveOgn()}
>
Approve staking contract to move OGN
</div>
</div>
<h1 className="mt-5">Compensation</h1>
<div>
Is contract adjuster locked:{' '}
<b>
{adjusterLocked === null
? 'Loading'
: adjusterLocked.toString()}
</b>
</div>
<div>Total claims in the contract: {compensationTotalClaims}</div>
<div>
Below actions can only be started using a governor account. To
get that account see the mnemonic in harhat.config.js and fetch
the first account
</div>
<h1 className="mt-5">Flipper</h1>
<div>
<div className="mb-2">Balance of coins on Flipper contract</div>
<table className="table table-bordered">
<thead>
<tr>
<td>Asset</td>
<td>Balance</td>
<td>Allowance</td>
</tr>
</thead>
<tbody>
{[...Object.keys(currencies), 'ousd'].map((coin) => {
const name = coin.toUpperCase()
const coinToDecimals = {
usdt: 6,
dai: 18,
ousd: 18,
usdc: 6,
}
const flipperBalance = flipperData[`${coin}Balance`]
const flipperAllowance = flipperData[`${coin}Allowance`]
return (
<tr key={name}>
<td>{name}</td>
<td>
{flipperBalance
? formatCurrency(
ethers.utils.formatUnits(
flipperBalance,
coinToDecimals[coin]
)
)
: 'Loading'}
</td>
<td>
{flipperAllowance
? flipperAllowance === '0.0'
? flipperAllowance
: 'Max'
: 'Loading'}
</td>
</tr>
)
})}
</tbody>
</table>
<div>
Make sure you have stablecoin funds available on your wallet
before transfering
<div className="d-flex flex-wrap">
<div
className="btn btn-primary my-4 mr-3"
onClick={() => sendCoinToFlipper(usdt, 1000)}
>
Fund with 1,000 USDT
</div>
<div
className="btn btn-primary my-4 mr-3"
onClick={() => sendCoinToFlipper(usdt, 100000)}
>
Fund with 100,000 USDT
</div>
<div
className="btn btn-primary my-4 mr-3"
onClick={() => sendCoinToFlipper(dai, 1000)}
>
Fund with 1,000 DAI
</div>
<div
className="btn btn-primary my-4 mr-3"
onClick={() => sendCoinToFlipper(dai, 100000)}
>
Fund with 100,000 DAI
</div>
</div>
<div className="d-flex flex-wrap">
<div
className="btn btn-primary my-4 mr-3"
onClick={() => sendCoinToFlipper(usdc, 1000)}
>
Fund with 1,000 USDC
</div>
<div
className="btn btn-primary my-4 mr-3"
onClick={() => sendCoinToFlipper(usdc, 100000)}
>
Fund with 100,000 USDC
</div>
<div
className="btn btn-primary my-4 mr-3"
onClick={() => sendCoinToFlipper(ousd, 1000)}
>
Fund with 1,000 OUSD
</div>
<div
className="btn btn-primary my-4 mr-3"
onClick={() => sendCoinToFlipper(ousd, 100000)}
>
Fund with 100,000 OUSD
</div>
</div>
<div className="d-flex flex-wrap">
<div
className="btn btn-primary my-4 mr-3"
onClick={() => approveFlipper(ousd)}
>
Approve OUSD
</div>
<div
className="btn btn-primary my-4 mr-3"
onClick={() => approveFlipper(usdt)}
>
Approve USDT
</div>
<div
className="btn btn-primary my-4 mr-3"
onClick={() => approveFlipper(usdc)}
>
Approve USDC
</div>
<div
className="btn btn-primary my-4 mr-3"
onClick={() => approveFlipper(dai)}
>
Approve DAI
</div>
{/* Flipper uses amounts denominated in 1e18 */}
<div
className="btn btn-primary my-4 mr-3"
onClick={() =>
swapFlipperUsdtToOusd(ethers.utils.parseUnits('1', 18))
}
>
Swap 1 USDT for OUSD
</div>
</div>
</div>
</div>
<h1 className="mt-5">Uniswap V3</h1>
<h3 className="mt-5">
Router and Liquidity manager general actions:
</h3>
<div className="d-flex flex-wrap">
<div
className="btn btn-primary my-4 mr-3"
onClick={() => approveForUniswapV3Router(usdt)}
>
Approve USDT Router
</div>
<div
className="btn btn-primary my-4 mr-3"
onClick={() => approveForUniswapV3Router(ousd)}
>
Approve OUSD Router
</div>
<div
className="btn btn-primary my-4 mr-3"
onClick={() => approveForUniswapV3Router(usdc)}
>
Approve USDC Router
</div>
<div
className="btn btn-primary my-4 mr-3"
onClick={() => approveForUniswapV3Router(dai)}
>
Approve DAI Router
</div>
<div
className="btn btn-primary my-4 mr-3"
onClick={() => approveForUniswapV3Manager(usdt)}
>
Approve USDT Manager
</div>
<div
className="btn btn-primary my-4 mr-3"
onClick={() => approveForUniswapV3Manager(ousd)}
>
Approve OUSD Manager
</div>
<div
className="btn btn-primary my-4 mr-3"
onClick={() => approveForUniswapV3Manager(usdc)}
>
Approve USDC Manager
</div>
<div
className="btn btn-primary my-4 mr-3"
onClick={() => approveForUniswapV3Manager(dai)}
>
Approve DAI Manager
</div>
</div>
{displayPoolInfo('ousd', 'usdt', ousd, usdt, uniV3OusdUsdt)}
{displayPoolInfo('dai', 'usdt', dai, usdt, uniV3DaiUsdt)}
{displayPoolInfo('usdc', 'usdt', usdc, usdt, uniV3UsdcUsdt)}
{!isProduction && (
<>
<h1 className="mt-5">Utils</h1>
<div>
<div className="d-flex flex-wrap">
<div
className="btn btn-primary my-4 mr-3"
onClick={() => setRedeemFee(50)}
>
Set redeemFee on Vault to 0.5%
</div>
</div>
</div>
</>
)}
</>
)}
</div>
<h1 className="mt-5">Liquidity mining</h1>
{isProduction && (
<h2>
Pool debug information not available in production environment
</h2>
)}
{!isProduction &&
pools &&
pools.map((pool) => {
const lp_token_allowance = Number(pool.lp_token_allowance)
const lp_token_allowance_unlimited =
lp_token_allowance && lp_token_allowance > Number.MAX_SAFE_INTEGER
return (
<div key={pool.name}>
<h2 className="mt-5">{pool.name} pool</h2>
<table className="table table-bordered">
<thead>
<tr>
<td>Pool stablecoin</td>
<td>Balance</td>
<td>Allowance</td>
</tr>
</thead>
<tbody>
{[pool.coin_one, pool.coin_two].map((coin) => {
const name = coin.name.toUpperCase()
const balance = Number(coin.balance)
const allowance = Number(coin.allowance)
const unlimited =
allowance && allowance > Number.MAX_SAFE_INTEGER
return (
<tr key={name}>
<td>{name}</td>
<td>{formatCurrency(balance)}</td>
<td>
{unlimited ? 'Max' : formatCurrency(allowance)}
</td>
</tr>
)
})}
</tbody>
</table>
<div className="d-flex flex-wrap">
{
<div
className="btn btn-primary my-4 mr-3"
disabled={pool.coin_one.name === 'OUSD'}
onClick={async () => {
if (pool.coin_one.name === 'OUSD') {
return
}
await pool.coin_one.contract.mint(
ethers.utils.parseUnits(
randomAmount(100000),
await pool.coin_one.contract.decimals()
)
)
}}
>
{pool.coin_one.name !== 'OUSD' && (
<>Mint Bazillion {pool.coin_one.name}</>
)}
{pool.coin_one.name === 'OUSD' && (
<>Mint OUSD from the dapp</>
)}
</div>
}
<div
className="btn btn-primary my-4 mr-3"
onClick={async () => {
await pool.coin_two.contract.mint(
ethers.utils.parseUnits(
randomAmount(100000),
await pool.coin_two.contract.decimals()
)
)
}}
>
Mint Bazillion {pool.coin_two.name}
</div>
<div
className="btn btn-primary my-4 mr-3"
onClick={async () => {
await pool.coin_one.contract.approve(
pool.lpContract.address,
ethers.constants.MaxUint256
)
}}
>
Approve {pool.coin_one.name}
</div>
<div
className="btn btn-primary my-4 mr-3"
onClick={async () => {
await pool.coin_two.contract.approve(
pool.lpContract.address,
ethers.constants.MaxUint256
)
}}
>
Approve {pool.coin_two.name}
</div>
</div>
<table className="table table-bordered">
<thead>
<tr>
<td>Token name</td>
<td>user's LP token Balance</td>
<td>Pool allowance (of LP token)</td>
<td>Staked tokens</td>
<td>Unclaimed OGN</td>
<td>Your weekly rate</td>
<td>Total pool deposits</td>
<td>Pool reward per block</td>
</tr>
</thead>
<tbody>
<tr>
<td>{pool.name}</td>
<td>{formatCurrency(pool.lp_tokens)}</td>
<td>
{lp_token_allowance_unlimited
? 'Max'
: formatCurrency(lp_token_allowance)}
</td>
<td>{formatCurrency(pool.staked_lp_tokens)}</td>
<td>{formatCurrency(pool.claimable_ogn)}</td>
<td>{formatCurrency(pool.your_weekly_rate)}</td>
<td>{formatCurrency(pool.pool_deposits)}</td>
<td>{formatCurrency(pool.reward_per_block)}</td>
</tr>
</tbody>
</table>
<div className="d-flex flex-wrap">
<div
className="btn btn-primary my-4 mr-3"
onClick={async () => {
await pool.lpContract.mint(
ethers.utils.parseUnits(
'1000.0',
await pool.lpContract.decimals()
)
)
}}
>
Mint LP token
</div>
<div
className="btn btn-primary my-4 mr-3"
onClick={async () => {
await pool.lpContract.approve(
pool.contract.address,
ethers.constants.MaxUint256
)
}}
>
Approve LP token (for pool)
</div>
<div
className="btn btn-primary my-4 mr-3"
onClick={async () => {
await pool.lpContract.decreaseAllowance(
pool.contract.address,
ethers.utils.parseUnits(
pool.lp_token_allowance,
await pool.lpContract.decimals()
)
)
}}
>
Clear LP token allowance (for pool)
</div>
<div
className="btn btn-primary my-4 mr-3"
onClick={async () => {
await pool.contract.deposit(
ethers.utils.parseUnits(
'51.0',
await pool.lpContract.decimals()
)
)
}}
>
Stake some LP tokens
</div>
<div
className="btn btn-primary my-4 mr-3"
onClick={async () => {
await pool.contract.claim()
}}
>
Claim OGN
</div>
</div>
</div>
)
})}
</Layout>
<style jsx>{`
.home {
padding-top: 80px;
}
table {
background-color: white;
}
@media (max-width: 799px) {
.home {
padding: 0;
}
}
`}</style>
</>
)
}
Example #20
Source File: index.js From Artion-Client with GNU General Public License v3.0 | 4 votes |
PaintBoard = () => {
const dispatch = useDispatch();
const history = useHistory();
const {
explorerUrl,
apiUrl,
fetchMintableCollections,
getNonce,
addUnlockableContent,
checkBan,
} = useApi();
const { registerRoyalty } = useSalesContract();
const { loadContract } = useContract();
const { account, chainId } = useWeb3React();
const imageRef = useRef();
const [selected, setSelected] = useState([]);
const [collections, setCollections] = useState([]);
const [nft, setNft] = useState();
const [type, setType] = useState();
const [image, setImage] = useState(null);
const [fee, setFee] = useState(null);
const [name, setName] = useState('');
const [symbol, setSymbol] = useState('');
const [description, setDescription] = useState('');
const [royalty, setRoyalty] = useState('');
const [xtra, setXtra] = useState('');
const [supply, setSupply] = useState(0);
const [hasUnlockableContent, setHasUnlockableContent] = useState(false);
const [unlockableContent, setUnlockableContent] = useState('');
const [currentMintingStep, setCurrentMintingStep] = useState(0);
const [isMinting, setIsMinting] = useState(false);
const [lastMintedTnxId, setLastMintedTnxId] = useState('');
const authToken = useSelector(state => state.ConnectWallet.authToken);
const getFee = async () => {
setFee(null);
try {
const contract = await loadContract(nft, FEE_ABI);
const _fee = await contract.platformFee();
setFee(parseFloat(_fee.toString()) / 10 ** 18);
} catch {
setFee(0);
}
};
const getCollections = async () => {
try {
const { data } = await fetchMintableCollections(authToken);
setCollections(data);
if (data.length) {
setSelected([data[0]]);
}
} catch (err) {
console.log(err);
}
};
useEffect(() => {
if (authToken) {
getCollections();
}
}, [authToken]);
useEffect(() => {
if (!nft) return;
getFee();
}, [nft]);
useEffect(() => {
dispatch(HeaderActions.toggleSearchbar(true));
}, []);
const onDrop = useCallback(acceptedFiles => {
setImage(acceptedFiles[0]);
}, []);
const { getRootProps, getInputProps } = useDropzone({
accept: accept.join(', '),
multiple: false,
onDrop,
maxSize: 15728640,
});
const removeImage = () => {
setImage(null);
if (imageRef.current) {
imageRef.current.value = '';
}
};
const imageToBase64 = () => {
return new Promise((resolve, reject) => {
let reader = new FileReader();
reader.readAsDataURL(image);
reader.onload = () => {
resolve(reader.result);
};
reader.onerror = err => {
reject(err);
};
});
};
const validateMetadata = () => {
return name !== '' && account !== '' && image;
};
const resetMintingStatus = () => {
setTimeout(() => {
setIsMinting(false);
setCurrentMintingStep(0);
}, 1000);
};
const mintNFT = async () => {
if (!account) {
showToast('info', 'Connect your wallet first');
return;
}
if (chainId !== ChainId.FANTOM && chainId !== ChainId.FANTOM_TESTNET) {
showToast('info', 'You are not connected to Fantom Opera Network');
return;
}
const balance = await WalletUtils.checkBalance(account);
if (balance < fee) {
showToast(
'custom',
`Your balance should be at least ${fee} FTM to mint an NFT`
);
return;
}
let isBanned = await checkBan(account, authToken);
if (isBanned) {
showToast('error', 'You are banned from minting');
return;
}
setLastMintedTnxId('');
// show stepper
setIsMinting(true);
console.log('created from ', account);
if (!validateMetadata()) {
resetMintingStatus();
return;
}
let signature;
let addr;
if (hasUnlockableContent && unlockableContent.length > 0) {
const { data: nonce } = await getNonce(account, authToken);
try {
const signer = await getSigner();
const msg = `Approve Signature on Artion.io with nonce ${nonce}`;
signature = await signer.signMessage(msg);
addr = ethers.utils.verifyMessage(msg, signature);
} catch (err) {
showToast(
'error',
'You need to sign the message to be able to update account settings.'
);
resetMintingStatus();
return;
}
}
let formData = new FormData();
const base64 = await imageToBase64();
formData.append('image', base64);
formData.append('name', name);
formData.append('account', account);
formData.append('description', description);
formData.append('symbol', symbol);
formData.append('xtra', xtra);
const _royalty = parseInt(royalty) * 100;
formData.append('royalty', isNaN(_royalty) ? 0 : _royalty);
try {
let result = await axios({
method: 'post',
url: `${apiUrl}/ipfs/uploadImage2Server`,
data: formData,
headers: {
'Content-Type': 'multipart/form-data',
Authorization: 'Bearer ' + authToken,
},
});
console.log('upload image result is ');
const jsonHash = result.data.jsonHash;
const contract = await loadContract(
nft,
type === 721 ? SINGLE_NFT_ABI : MULTI_NFT_ABI
);
try {
const args =
type === 721 ? [account, jsonHash] : [account, supply, jsonHash];
let tx;
if (!fee) {
tx = await contract.mint(...args);
} else {
const options = {
value: ethers.utils.parseEther(fee.toString()),
gasPrice: getHigherGWEI(),
};
const gasEstimate = await contract.estimateGas.mint(...args, options);
options.gasLimit = calculateGasMargin(gasEstimate);
tx = await contract.mint(...args, options);
}
setCurrentMintingStep(1);
setLastMintedTnxId(tx.hash);
setCurrentMintingStep(2);
const confirmedTnx = await tx.wait();
setCurrentMintingStep(3);
let mintedTkId;
if (type === 721) {
const evtCaught = confirmedTnx.logs[0].topics;
mintedTkId = BigNumber.from(evtCaught[3]);
} else {
mintedTkId = BigNumber.from(
ethers.utils.hexDataSlice(confirmedTnx.logs[1].data, 0, 32)
);
}
const royaltyTx = await registerRoyalty(
nft,
mintedTkId.toNumber(),
isNaN(_royalty) ? 0 : _royalty
);
await royaltyTx.wait();
// save unlockable content
if (hasUnlockableContent && unlockableContent.length > 0) {
await addUnlockableContent(
nft,
mintedTkId.toNumber(),
unlockableContent,
signature,
addr,
authToken
);
}
showToast('success', 'New NFT item minted!');
removeImage();
setName('');
setSymbol('');
setDescription('');
setTimeout(() => {
history.push(`/explore/${nft}/${mintedTkId.toNumber()}`);
}, 1000 + Math.random() * 2000);
} catch (error) {
showToast('error', formatError(error));
}
} catch (error) {
showToast('error', error.message);
}
resetMintingStatus();
};
return (
<div className={styles.container}>
<Header border />
<div className={styles.body}>
<div className={styles.board}>
<div {...getRootProps({ className: styles.uploadCont })}>
<input {...getInputProps()} ref={imageRef} />
{image ? (
<>
<img
className={styles.image}
src={URL.createObjectURL(image)}
/>
<div className={styles.overlay}>
<CloseIcon className={styles.remove} onClick={removeImage} />
</div>
</>
) : (
<>
<div className={styles.uploadtitle}>
Drop files here or
<span
className={styles.browse}
onClick={() => imageRef.current?.click()}
>
browse
</span>
</div>
<div className={styles.uploadsubtitle}>
JPG, PNG, BMP, GIF Max 15mb.
</div>
</>
)}
</div>
</div>
<div className={styles.panel}>
<div className={styles.panelInputs}>
<div className={styles.panelLeft}>
<div className={styles.formGroup}>
<p className={styles.formLabel}>Collection</p>
<Select
options={collections}
disabled={isMinting}
values={selected}
onChange={([col]) => {
setSelected([col]);
setNft(col.erc721Address);
setType(col.type);
}}
className={styles.select}
placeholder="Choose Collection"
itemRenderer={({ item, methods }) => (
<div
key={item.erc721Address}
className={styles.collection}
onClick={() => {
methods.clearAll();
methods.addItem(item);
}}
>
<img
src={`https://cloudflare-ipfs.com/ipfs/${item.logoImageHash}`}
className={styles.collectionLogo}
/>
<div className={styles.collectionName}>
{item.collectionName}
</div>
</div>
)}
contentRenderer={({ props: { values } }) =>
values.length > 0 ? (
<div className={styles.collection}>
<img
src={`https://cloudflare-ipfs.com/ipfs/${values[0].logoImageHash}`}
className={styles.collectionLogo}
/>
<div className={styles.collectionName}>
{values[0].collectionName}
</div>
</div>
) : (
<div className={styles.collection} />
)
}
/>
</div>
<div className={styles.formGroup}>
<p className={styles.formLabel}>Name</p>
<input
className={styles.formInput}
maxLength={40}
placeholder="Name"
value={name}
onChange={e => setName(e.target.value)}
disabled={isMinting}
/>
<div className={styles.lengthIndicator}>{name.length}/40</div>
</div>
<div className={styles.formGroup}>
<p className={styles.formLabel}>Symbol</p>
<input
className={styles.formInput}
maxLength={20}
placeholder="Symbol"
value={symbol}
onChange={e => setSymbol(e.target.value)}
disabled={isMinting}
/>
<div className={styles.lengthIndicator}>{symbol.length}/20</div>
</div>
<div className={styles.formGroup}>
<p className={styles.formLabel}>Description</p>
<textarea
className={cx(styles.formInput, styles.longInput)}
maxLength={120}
placeholder="Description"
value={description}
onChange={e => setDescription(e.target.value)}
disabled={isMinting}
/>
<div className={styles.lengthIndicator}>
{description.length}/120
</div>
</div>
</div>
<div className={styles.panelRight}>
{type === 1155 && (
<div className={styles.formGroup}>
<p className={styles.formLabel}>Supply</p>
<PriceInput
className={styles.formInput}
placeholder="Supply"
decimals={0}
value={'' + supply}
onChange={setSupply}
disabled={isMinting}
/>
</div>
)}
<div className={styles.formGroup}>
<p className={styles.formLabel}>
Royalty (%)
<BootstrapTooltip
title="If you set a royalty here, you will get X percent of sales price each time an NFT is sold on our platform."
placement="top"
>
<HelpOutlineIcon />
</BootstrapTooltip>
</p>
<PriceInput
className={styles.formInput}
placeholder="Royalty"
decimals={2}
value={'' + royalty}
onChange={val =>
val[val.length - 1] === '.'
? setRoyalty(val)
: setRoyalty(Math.min(100, +val))
}
disabled={isMinting}
/>
</div>
<div className={styles.formGroup}>
<p className={styles.formLabel}>
Link to IP Rights Document (Optional)
<BootstrapTooltip
title="Link to the document which proves your ownership of this image."
placement="top"
>
<HelpOutlineIcon />
</BootstrapTooltip>
</p>
<input
className={styles.formInput}
placeholder="Enter Link"
value={xtra}
onChange={e => setXtra(e.target.value)}
disabled={isMinting}
/>
</div>
<div className={styles.formGroup}>
<p className={styles.formLabel}>
Unlockable Content
<PurpleSwitch
checked={hasUnlockableContent}
onChange={e => {
setHasUnlockableContent(e.target.checked);
setUnlockableContent('');
}}
name="unlockableContent"
/>
</p>
{hasUnlockableContent && (
<textarea
className={cx(styles.formInput, styles.longInput)}
maxLength={500}
placeholder="Unlockable Content"
value={unlockableContent}
onChange={e => setUnlockableContent(e.target.value)}
disabled={isMinting}
/>
)}
</div>
</div>
</div>
{isMinting && (
<div>
<Stepper activeStep={currentMintingStep} alternativeLabel>
{mintSteps.map(label => (
<Step key={label}>
<StepLabel>{label}</StepLabel>
</Step>
))}
</Stepper>
</div>
)}
<div
className={cx(
styles.button,
(isMinting || !account || !validateMetadata()) && styles.disabled
)}
onClick={
isMinting || !account || !validateMetadata() ? null : mintNFT
}
>
{isMinting ? (
<ClipLoader size="16" color="white"></ClipLoader>
) : (
'Mint'
)}
</div>
<div className={styles.fee}>
{fee !== null ? (
<>
<InfoIcon />
{fee} FTM are charged to create a new NFT.
</>
) : (
<Skeleton width={330} height={22} />
)}
</div>
<div className={styles.mintStatusContainer}>
{lastMintedTnxId !== '' && (
<a
className={styles.tnxAnchor}
target="_blank"
rel="noopener noreferrer"
href={`${explorerUrl}/tx/${lastMintedTnxId}`}
>
You can track the last transaction here ...
</a>
)}
</div>
</div>
</div>
</div>
);
}
Example #21
Source File: preDungeonCheck.js From ethernal with MIT License | 4 votes |
store = derived(
wallet,
async ($wallet, set) => {
const _set = obj => {
$data = { ...$data, ...obj };
console.log('pre dungeon check', $data);
set($data);
};
if ($wallet.status === 'Ready') {
if (lastWalletAddress !== $wallet.address) {
lastWalletAddress = $wallet.address;
_set({ status: 'Loading' });
const delegateAccount = getDelegateKey($wallet.address);
const checkCharacter = async () => {
const characterId = await wallet.call('Player', 'getLastCharacterId', $wallet.address);
const isDelegateReady = await wallet.call(
'Player',
'isDelegateFor',
delegateAccount.address,
$wallet.address,
);
const result = await wallet.call('Characters', 'fullOwnerOf', characterId);
const isCharacterInDungeon =
result.owner === wallet.getContract('Dungeon').address &&
result.subOwner.eq(BigNumber.from($wallet.address));
const balance = await wallet.getProvider().getBalance($wallet.address);
// TODO should be free
const insufficientBalance = balance.lt('1100000000000000000');
return { characterId, isDelegateReady, isCharacterInDungeon, insufficientBalance };
};
const { characterId, isDelegateReady, isCharacterInDungeon, insufficientBalance } = await checkCharacter();
let characterInfo;
const { minBalance } = config($wallet.chainId);
let refill = minBalance;
try {
characterInfo = await fetchCache(`characters/${characterId}`);
} catch (e) {
console.log('failed to fetch character info from cache');
}
let ressurectedId;
if (characterInfo && !isCharacterInDungeon && characterInfo.status.status === 'dead') {
const { Dungeon } = window.contracts; // TODO get contract elsewhere
const topic = Dungeon.interface.getEventTopic(Dungeon.interface.events['Resurrect(uint256,uint256)']);
const [ressurect] = await Dungeon.queryFilter({
address: Dungeon.address,
topics: [topic, uint256(characterId)],
});
if (ressurect) {
ressurectedId = ressurect.args.newCharacterId;
}
}
_set({
status: 'Done',
isDelegateReady,
isCharacterInDungeon,
characterId,
characterInfo,
ressurectedId,
refill,
insufficientBalance,
});
if (isCharacterInDungeon) {
preDungeon.clear();
characterChoice.clear();
}
store.checkBackIn = async value => {
const gasEstimate = 4000000; // @TODO: proper estimation
_set({ status: 'SigningBackIn', delegateAccount });
let tx;
try {
tx = await wallet.tx(
{ gas: gasEstimate + 15000, gasPrice, value },
'Player',
'addDelegate',
delegateAccount.address,
);
await tx.wait();
} catch (e) {
_set({ status: 'Error', error: { code: 'addDelegate', message: e.toString(), e, wallet } }); // TODO
}
const { isDelegateReady, isCharacterInDungeon, insufficientBalance } = await checkCharacter();
_set({
status: 'Done',
isDelegateReady,
isCharacterInDungeon,
insufficientBalance,
});
};
store.enter = async ({ ressurectedId, characterInfo }) => {
const { location } = await fetchCache('entry');
await wallet
.tx(
{ gas: BigNumber.from(2000000).toHexString(), gasPrice },
'Player',
'enter',
'0x0000000000000000000000000000000000000000',
ressurectedId,
'0',
characterInfo.characterName,
'0',
location || coordinatesToLocation('0,0'),
)
.then(tx => tx.wait());
const { isDelegateReady, isCharacterInDungeon, insufficientBalance } = await checkCharacter();
_set({
status: 'Done',
isDelegateReady,
isCharacterInDungeon,
insufficientBalance,
});
};
store.join = async ({ name, characterClass }) => {
_set({ status: 'Joining' });
const gasEstimate = BigNumber.from(2000000).toHexString();
const { price } = config($wallet.chainId);
const value = BigNumber.from(price).toHexString();
const { location } = await fetchCache('entry');
const tx = await wallet.tx(
{ gas: gasEstimate, gasPrice, value },
'Player',
'createAndEnter',
delegateAccount.address,
0,
name,
characterClass,
location || coordinatesToLocation('0,0'),
);
const receipt = await tx.wait();
console.log({ receipt });
console.log('gas used for join', BigNumber.from(receipt.gasUsed).toString());
const { isCharacterInDungeon, isDelegateReady } = await checkCharacter();
if (isCharacterInDungeon) {
preDungeon.clear();
characterChoice.clear();
}
_set({
firstTime: true,
status: 'Done',
isDelegateReady,
isCharacterInDungeon,
});
};
}
} else {
lastWalletAddress = null;
_set({ status: 'None' });
}
},
$data,
)
Example #22
Source File: claim.js From ethernal with MIT License | 4 votes |
store = derived(
wallet,
async ($wallet, set) => {
const _set = obj => {
$claim = { ...$claim, ...obj };
log.info('CLAIM', JSON.stringify($claim, null, ' '));
set($claim);
};
const gasPrice = BigNumber.from('1000000000'); // await provider.getGasPrice();
const gasLimit = BigNumber.from(21000);
const gasFee = gasLimit.mul(gasPrice);
const extraValue = BigNumber.from('100000000000000');
const minimum = gasFee.add(extraValue);
const maximum = BigNumber.from('4000000000000000000'); // @TODO: config)
if (claimKey && typeof $claim.rawBalance === 'undefined') {
try {
claimWallet = new Wallet(claimKey);
const provider = wallet.getFallbackProvider();
if (provider) {
(async () => {
let claimBalance = await wallet.getFallbackProvider().getBalance(claimWallet.address);
if (claimBalance.lt(minimum)) {
claimBalance = BigNumber.from(0);
}
if (claimBalance.gt(maximum)) {
claimBalance = maximum;
}
// eslint-disable-next-line no-console
console.log({
address: claimWallet.address,
status: 'WaitingWallet',
rawBalance: claimBalance,
balance: utils.formatUnits(claimBalance, 18),
});
_set({
status: 'WaitingWallet',
rawBalance: claimBalance,
balance: utils.formatUnits(claimBalance, 18),
});
})();
}
} catch (e) {
const claimBalance = BigNumber.from(0);
_set({
status: 'WaitingWallet',
rawBalance: claimBalance,
balance: utils.formatUnits(claimBalance, 18),
});
}
}
async function claim() {
_set({ status: 'Loading' });
const provider = wallet.getProvider();
let claimingTxHash;
const localStorageKeyForClaimTxHash = `${$wallet.address}_${$wallet.chainId}_claimTxHash`;
try {
claimingTxHash = localStorage.getItem(localStorageKeyForClaimTxHash);
} catch (err) {
//
}
if (claimingTxHash && claimingTxHash !== '') {
_set({ status: 'WaitingOldTx' });
const tx = await provider.getTransaction(claimingTxHash);
if (tx) {
const receipt = await tx.wait();
if (tx.blockNumber) {
if (receipt.status === 1) {
_set({ status: 'Claimed' });
clearClaimKey();
return;
}
_set({ status: 'Failed' });
} else {
const txReceipt = await tx.wait();
if (txReceipt.status === 1) {
_set({ status: 'Claimed' });
clearClaimKey();
return;
}
_set({ status: 'Failed' });
}
} else {
log.trace(`cannot find tx ${claimingTxHash}`);
}
}
const claimBalance = await provider.getBalance(claimWallet.address);
log.trace({ claimBalance });
const claimValue = BigNumber.from('5000000000000000000'); // @TODO: from Config 5 DAI
if (claimBalance.gte(minimum)) {
const signer = claimWallet.connect(provider);
let value = claimBalance.sub(gasFee);
const maxValue = BigNumber.from(claimValue);
if (value.gt(maxValue)) {
value = maxValue;
}
_set({ status: 'Claiming' });
const tx = await signer.sendTransaction({
to: $wallet.address,
value,
gasLimit,
gasPrice,
});
localStorage.setItem(localStorageKeyForClaimTxHash, tx.hash);
_set({ status: 'WaitingTx' });
const receipt = await tx.wait();
if (receipt.status === 1) {
_set({ status: 'Claimed' });
clearClaimKey();
return;
}
_set({ status: 'Failed' });
} else {
_set({ status: 'Gone' });
}
clearClaimKey();
}
store.claim = claim;
store.acknowledge = () => {
_set({ status: 'None' });
};
},
$claim,
)