Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Providers patch #602

Merged
merged 14 commits into from
Jul 27, 2021
31 changes: 31 additions & 0 deletions src/Root.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
import { defineComponent, h } from 'vue';
import App from './App.vue';
import * as providerMap from './providers';

const providers = Object.values(providerMap);

export default defineComponent({
components: {
App,
...providerMap
},

render() {
function renderProviders(providers) {
if (!providers.length) return h(App);

const [provider, ...remainingProviders] = providers;
return h(
provider,
{},
{
default() {
return [renderProviders(remainingProviders)];
}
}
);
}

return renderProviders(providers);
}
});
13 changes: 8 additions & 5 deletions src/components/modals/SelectTokenModal/SelectTokenModal.vue
Original file line number Diff line number Diff line change
Expand Up @@ -108,7 +108,7 @@ import { defineComponent, reactive, toRefs, computed } from 'vue';
import { useStore } from 'vuex';
import { useI18n } from 'vue-i18n';
import { isAddress, getAddress } from '@ethersproject/address';
import useTokenLists from '@/composables/useTokensStore';
import useTokenStore from '@/composables/useTokensStore';
// import useTokenLists2 from '@/composables/useTokenLists2';
import TokenListItem from '@/components/lists/TokenListItem.vue';
import TokenListsListItem from '@/components/lists/TokenListsListItem.vue';
Expand Down Expand Up @@ -147,15 +147,18 @@ export default defineComponent({
isActiveList,
listMap,
activeTokenLists
} = useTokenLists();
const { tokens: tokenMap } = useTokens(data);
const tokens = computed(() => Object.values(tokenMap.value));
} = useTokenStore();

const { tokens: tokenMap } = useTokens(toRefs(data));
const tokens = computed(() => {
return Object.values(tokenMap.value);
});
// const {
// approvedTokenLists,
// toggleList,
// toggled: toggledTokenLists,
// isToggled: isToggledList
// } = useTokenLists2();
// } = useTokenStore2();

// COMPOSABLES
const store = useStore();
Expand Down
4 changes: 2 additions & 2 deletions src/composables/trade/useTokenApproval.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { computed, ComputedRef, Ref, ref, watch } from 'vue';
import { computed, Ref, ref, watch } from 'vue';
import { useI18n } from 'vue-i18n';
import { parseUnits } from '@ethersproject/units';
import { TransactionResponse } from '@ethersproject/providers';
Expand All @@ -18,7 +18,7 @@ import { TokenMap } from '@/types';
export default function useTokenApproval(
tokenInAddress: Ref<string>,
amount: Ref<string>,
tokens: ComputedRef<TokenMap>
tokens: Ref<TokenMap>
) {
const approving = ref(false);
const approved = ref(false);
Expand Down
4 changes: 2 additions & 2 deletions src/composables/trade/useValidation.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { computed, ComputedRef, Ref } from 'vue';
import { computed, Ref } from 'vue';

import useWeb3 from '@/services/web3/useWeb3';
import { configService } from '@/services/config/config.service';
Expand All @@ -21,7 +21,7 @@ export default function useValidation(
tokenInAmount: Ref<string>,
tokenOutAddress: Ref<string>,
tokenOutAmount: Ref<string>,
tokens: ComputedRef<TokenMap>
tokens: Ref<TokenMap>
) {
const { isWalletReady } = useWeb3();

Expand Down
87 changes: 9 additions & 78 deletions src/composables/useAccountBalances.ts
Original file line number Diff line number Diff line change
@@ -1,90 +1,21 @@
import getProvider from '@/lib/utils/provider';
import { useQuery } from 'vue-query';
import { computed, reactive } from 'vue';
import { getBalances } from '@/lib/utils/balancer/tokens';
import { formatEther, formatUnits } from '@ethersproject/units';
import { getAddress } from '@ethersproject/address';
import QUERY_KEYS from '@/constants/queryKeys';
import { ETHER } from '@/constants/tokenlists';
import useWeb3 from '@/services/web3/useWeb3';
import useTokenStore from './useTokensStore';
import {
BalancesProviderPayload,
BalancesProviderSymbol
} from '@/providers/balances.provider';
import { inject } from 'vue';

// THE CONTENTS OF THIS WILL BE REPLACED/ALTERED WITH THE REGISTRY REFACTOR
export default function useAccountBalances() {
const { account, userNetworkConfig, isWalletReady } = useWeb3();
const { allTokens: tokens, isLoading: isLoadingTokens } = useTokenStore();

const isQueryEnabled = computed(
() =>
account.value !== null &&
Object.keys(tokens).length !== 0 &&
isWalletReady.value &&
!isLoadingTokens.value
);

const {
data,
balances,
hasBalance,
error,
isLoading,
isIdle,
isError,
isFetching,
refetch: refetchBalances
} = useQuery(
reactive(QUERY_KEYS.Balances.All(account, userNetworkConfig, tokens)),
() => {
return Promise.all([
getBalances(
String(userNetworkConfig.value?.chainId),
getProvider(userNetworkConfig.value?.key),
account.value,
Object.values(tokens.value)
.map(token => token.address)
.filter(token => token !== ETHER.address)
),
getProvider(userNetworkConfig.value?.key).getBalance(
account.value.toLowerCase()
)
]);
},
reactive({
enabled: isQueryEnabled,
keepPreviousData: isWalletReady
})
);

const balances = computed(() => {
if (data.value) {
const balances = {};
Object.keys(data.value[0]).forEach((tokenAddress: string) => {
const balance = formatUnits(
data.value[0][tokenAddress],
tokens.value[getAddress(tokenAddress)]?.decimals || 18
);
// not concerned with tokens which have a 0 balance
if (balance === '0.0') return;
balances[tokenAddress] = {
balance,
symbol: tokens.value[getAddress(tokenAddress)].symbol,
address: getAddress(tokenAddress)
};
});

// separate case for native ether
balances[ETHER.address.toLowerCase()] = {
balance: formatEther(data.value[1] || 0),
symbol: ETHER.symbol,
address: ETHER.address
};
return balances;
}
return null;
});

function hasBalance(address: string): boolean {
return !!(balances.value || {})[address];
}

refetchBalances
} = inject(BalancesProviderSymbol) as BalancesProviderPayload;
return {
balances,
hasBalance,
Expand Down
93 changes: 17 additions & 76 deletions src/composables/useAllowances.ts
Original file line number Diff line number Diff line change
@@ -1,88 +1,29 @@
import useWeb3 from '@/services/web3/useWeb3';
import { useQuery } from 'vue-query';
import { getAllowances } from '@/lib/utils/balancer/tokens';
import getProvider from '@/lib/utils/provider';
import { computed, reactive, Ref, ref } from 'vue';
import { ETHER } from '@/constants/tokenlists';
import { isAddress } from 'web3-utils';
import QUERY_KEYS from '@/constants/queryKeys';
import useTokens from './useTokens';
import { inject, onBeforeMount, Ref } from 'vue';
import {
AllowancesProviderSymbol,
AllowancesProviderPayload
} from '@/providers/allowances.provider';

type UseAccountPayload = {
tokens?: Ref<string[]>;
dstList?: Ref<string[]>;
};

const dstAllowanceMap = ref({});

// THE CONTENTS OF THIS WILL BE REPLACED/ALTERED WITH THE REGISTRY REFACTOR
export default function useAllowances(payload?: UseAccountPayload) {
const { userNetworkConfig, account } = useWeb3();
const { tokens: allTokens } = useTokens();
const provider = getProvider(String(userNetworkConfig.value?.chainId));
// filter out ether and any bad addresses
const tokens = computed(() =>
(payload?.tokens?.value || Object.keys(allTokens.value)).filter(
t => t !== ETHER.address && isAddress(t)
)
);
const dstList = computed(() => [
...(payload?.dstList?.value || []),
userNetworkConfig.value?.addresses.vault
]);

const isQueryEnabled = computed(
() => account.value != '' && tokens.value.length > 0
);
const {
data: allowances,
isLoading,
isFetching,
refetch: refetchAllowances
} = useQuery(
QUERY_KEYS.Account.Allowances(userNetworkConfig, account, dstList, tokens),
() =>
Promise.all(
dstList.value.map(async dst =>
getAllowances(
String(userNetworkConfig.value?.chainId),
provider,
account.value,
dst,
tokens.value
)
)
),
reactive({
enabled: isQueryEnabled,
onSuccess: allowances => {
allowances.forEach((allowance, i) => {
dstAllowanceMap.value[dstList.value[i]] = allowance;
});
}
})
);

const isLoadingOrFetching = computed(
() => isLoading.value || isFetching.value
);

const getRequiredAllowances = query => {
const tokens = query.tokens;
const amounts = query.amounts;
const dst = query.dst || userNetworkConfig.value?.addresses.vault;

const requiredAllowances = tokens.filter((token, index) => {
const amount = amounts[index];
if (parseFloat(amount) == 0) return false;
if (!dstAllowanceMap.value) return false;
if (!dstAllowanceMap.value[dst]) return true;
if (!dstAllowanceMap.value[dst][token.toLowerCase()]) return true;
return dstAllowanceMap.value[dst][token.toLowerCase()].lt(amount);
});

return requiredAllowances;
};
allowances,
getRequiredAllowances,
isLoading: isLoadingOrFetching,
refetchAllowances,
updateAllowanceRequest
} = inject(AllowancesProviderSymbol) as AllowancesProviderPayload;

onBeforeMount(() => {
if (payload) {
updateAllowanceRequest(payload);
}
});

return {
allowances,
Expand Down
81 changes: 15 additions & 66 deletions src/composables/useTokens.ts
Original file line number Diff line number Diff line change
@@ -1,73 +1,22 @@
import { Token, TokenMap } from '@/types';
import { getAddress } from '@ethersproject/address';
import { keyBy, orderBy, uniqBy } from 'lodash';
import { computed } from 'vue';
import { useStore } from 'vuex';
import useAccountBalances from './useAccountBalances';
import useTokenStore from './useTokensStore';

type TokenRequest = {
query?: string;
queryAddress?: string;
};
import {
TokenRequest,
TokensProviderPayload,
TokensProviderSymbol
} from '@/providers/tokens.provider';
import { inject, onBeforeMount } from 'vue';

export default function useTokens(request?: TokenRequest) {
const store = useStore();
const prices = computed(() => store.state.market.prices);
const { allTokens: _allTokens } = useTokenStore();
const { balances } = useAccountBalances();

const tokensList = computed(() => {
const _tokens = uniqBy<Token>(
orderBy(
// populate token data into list of tokens
Object.values(_allTokens.value).map(token => {
const balance =
(balances.value || {})[token.address.toLowerCase()]?.balance || '0';
const price = prices.value[token.address.toLowerCase()]?.price || 0;
const value = balance * price;
const price24HChange =
prices.value[token.address.toLowerCase()]?.price24HChange || 0;
const value24HChange = (value / 100) * price24HChange;
return {
...token,
address: getAddress(token.address), // Enforce that we use checksummed addresses
value,
price,
price24HChange,
balance,
value24HChange
};
}),
['value', 'balance'],
['desc', 'desc']
),
'address'
);

if (request?.queryAddress) {
const queryAddressLC = request?.queryAddress?.toLowerCase();

return _tokens.filter(
token => token.address?.toLowerCase() === queryAddressLC
);
}

// search functionality, this can be better
if (request?.query) {
const queryLC = request?.query?.toLowerCase();
const { tokens, updateTokenRequest } = inject(
TokensProviderSymbol
) as TokensProviderPayload;

return _tokens.filter(
token =>
token.name.toLowerCase().includes(queryLC) ||
token.symbol.toLowerCase().includes(queryLC)
);
onBeforeMount(() => {
if (request) {
updateTokenRequest(request);
}

return _tokens;
});

const tokens = computed(() => keyBy(tokensList.value, 'address') as TokenMap);

return { tokens };
return {
tokens
};
}
Loading