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

zkSync bug fixes #7368

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 5 additions & 5 deletions app/assets/v2/js/abi.js

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

130 changes: 110 additions & 20 deletions app/assets/v2/js/cart.js
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ Vue.component('grants-cart', {
data: function() {
return {
// Checkout, shared
currentTokens: [], // list of all available tokens
adjustGitcoinFactor: false, // if true, show section for user to adjust Gitcoin's percentage
tokenList: undefined, // array of all tokens for selected network
isLoading: undefined,
Expand Down Expand Up @@ -310,17 +311,17 @@ Vue.component('grants-cart', {
zkSyncBlockExplorerUrl() {
// Flow A, zkScan link
if (this.hasSufficientZkSyncBalance) {
if (document.web3network === 'mainnet')
return `https://zkscan.io/explorer/accounts/${this.userAddress}`;
return `https://${document.web3network}.zkscan.io/explorer/accounts/${this.userAddress}`;
if (document.web3network === 'rinkeby')
return `https://${document.web3network}.zkscan.io/explorer/accounts/${this.userAddress}`;
return `https://zkscan.io/explorer/accounts/${this.userAddress}`;
}

// Flow B, etherscan link
if (!this.zkSyncDepositTxHash)
return undefined;
if (document.web3network === 'mainnet')
return `https://etherscan.io/tx/${this.zkSyncDepositTxHash}`;
return `https://${document.web3network}.etherscan.io/tx/${this.zkSyncDepositTxHash}`;
if (document.web3network === 'rinkeby')
return `https://${document.web3network}.etherscan.io/tx/${this.zkSyncDepositTxHash}`;
return `https://etherscan.io/tx/${this.zkSyncDepositTxHash}`;
},

// Array of supported tokens
Expand Down Expand Up @@ -656,23 +657,25 @@ Vue.component('grants-cart', {
},

/**
* @notice Get token address and decimals
* @notice Get token address and decimals using data fetched from the API endpoint in the
* mounted hook
* @dev We use this instead of tokenNameToDetails in tokens.js because we use a different
* address to represent ETH
* address to represent ETH. We also add additional fields that are not included in the
* response to facilitate backward compatibility
* @param {String} name Token name, e.g. ETH or DAI
*/
getTokenByName(name) {
if (name === 'ETH') {
return {
addr: ETH_ADDRESS,
address: ETH_ADDRESS,
name: 'ETH',
symbol: 'ETH',
decimals: 18,
priority: 1
};
}
var network = document.web3network;

return tokens(network).filter(token => token.name === name)[0];
return this.currentTokens.filter(token => token.name === name)[0];
},

/**
Expand Down Expand Up @@ -788,6 +791,9 @@ Vue.component('grants-cart', {

if (new BN(userEthBalance, 10).lt(new BN(this.donationInputsEthAmount, 10))) {
// User ETH balance is too small compared to selected donation amounts
this.zkSyncCheckoutStep1Status = 'not-started';
this.zkSyncCheckoutStep2Status = 'not-started';
this.showZkSyncModal = false;
throw new Error('Insufficient ETH balance to complete checkout');
}
// ETH balance is sufficient, continue to next iteration since no approval check
Expand All @@ -810,6 +816,9 @@ Vue.component('grants-cart', {

if (new BN(userTokenBalance, 10).lt(requiredAllowance)) {
// Balance is too small, exit checkout flow
this.zkSyncCheckoutStep1Status = 'not-started';
this.zkSyncCheckoutStep2Status = 'not-started';
this.showZkSyncModal = false;
throw new Error(`Insufficient ${tokenName} balance to complete checkout`, 'error');
}

Expand Down Expand Up @@ -1613,15 +1622,17 @@ Vue.component('grants-cart', {
*/
async setupZkSync() {
// Configure ethers and zkSync
const network = document.web3network || 'mainnet';

this.ethersProvider = new ethers.providers.Web3Provider(provider);
this.signer = this.ethersProvider.getSigner();
this.syncProvider = await zksync.getDefaultProvider(document.web3network, 'HTTP');
this.syncProvider = await zksync.getDefaultProvider(network, 'HTTP');
this.numberOfConfirmationsNeeded = await this.syncProvider.getConfirmationsForEthOpAmount();
this.zkSyncDonationInputsEthAmount = this.donationInputsEthAmount;
this.zkSyncWalletState = await this.syncProvider.getState(this.userAddress);

// Set zkSync contract address based on network
this.zkSyncContractAddress = document.web3network === 'mainnet'
this.zkSyncContractAddress = network === 'mainnet'
? zkSyncContractAddressMainnet // mainnet
: zkSyncContractAddressRinkeby; // rinkeby
},
Expand Down Expand Up @@ -1873,8 +1884,12 @@ Vue.component('grants-cart', {
.balanceOf(this.userAddress)
.call({from: this.userAddress});

if (BigNumber.from(userTokenBalance).lt(BigNumber.from(totalRequiredAmount)))
if (BigNumber.from(userTokenBalance).lt(BigNumber.from(totalRequiredAmount))) {
this.zkSyncCheckoutStep1Status = 'not-started';
this.zkSyncCheckoutStep2Status = 'not-started';
this.showZkSyncModal = false;
throw new Error(`Insufficient ${tokenSymbol} balance to complete checkout`, 'error');
}
}

// Add ETH additional deposit and ensure user has enough for donation + gas (use lte not lt)
Expand All @@ -1890,8 +1905,12 @@ Vue.component('grants-cart', {
this.zkSyncDonationInputsEthAmount = initialAmount.add(totalRequiredAmount).toString();
const userEthBalance = await web3.eth.getBalance(this.userAddress);

if (BigNumber.from(userEthBalance).lte(BigNumber.from(this.zkSyncDonationInputsEthAmount)))
if (BigNumber.from(userEthBalance).lte(BigNumber.from(this.zkSyncDonationInputsEthAmount))) {
this.zkSyncCheckoutStep1Status = 'not-started';
this.zkSyncCheckoutStep2Status = 'not-started';
this.showZkSyncModal = false;
throw new Error('Insufficient ETH balance to complete checkout');
}
}

// Otherwise, request approvals. As mentioned above, we check against userAddress
Expand Down Expand Up @@ -1983,27 +2002,31 @@ Vue.component('grants-cart', {
// Setup -------------------------------------------------------------------------------------
const ethAmount = this.zkSyncDonationInputsEthAmount; // amount of ETH being donated
const depositRecipient = this.gitcoinSyncWallet.address(); // address of deposit recipient
const BigNumber = ethers.BigNumber;

// Deposit funds ---------------------------------------------------------------------------
// Setup overrides
let overrides = { gasLimit: ethers.BigNumber.from(String(this.zkSyncDonationInputsGasLimit)) };
let overrides = {
gasLimit: BigNumber.from(String(this.zkSyncDonationInputsGasLimit)),
value: ethers.constants.Zero
};

if (ethers.BigNumber.from(ethAmount).gt('0')) {
if (BigNumber.from(ethAmount).gt('0')) {
// Specify how much ETH to send with transaction
overrides.value = await this.getTotalAmountToTransfer('ETH', ethAmount);
}

const selectedTokens = Object.keys(this.donationsToGrants);
let depositTx;


if (this.depositContractToUse === batchZkSyncDepositContractAddress) {
// If batch deposit ---------------------------------------------------------
// Deposit mix of tokens
console.log('Generating deposit payload...');
const deposits = []; // array of arrays, passed into batckZkSyncDepositContract.deposit(...)

// Handle ETH
if (ethers.BigNumber.from(ethAmount).gt('0'))
if (BigNumber.from(ethAmount).gt('0'))
deposits.push([ ETH_ADDRESS, overrides.value ]);

// Handle tokens
Expand Down Expand Up @@ -2033,13 +2056,53 @@ Vue.component('grants-cart', {
batchZkSyncDepositContractAbi,
this.signer
);

// Verify user has sufficient balances now that we account for transaction fees
// Check tokens
for (let i = 0; i < deposits.length; i += 1) {
const tokenContract = new web3.eth.Contract(token_abi, deposits[i][0]);
const requiredAmount = deposits[i][1];
const userTokenBalance = await tokenContract.methods.balanceOf(this.userAddress).call({from: this.userAddress});

if (BigNumber.from(userTokenBalance).lt(BigNumber.from(requiredAmount))) {
this.zkSyncCheckoutStep1Status = 'not-started';
this.zkSyncCheckoutStep2Status = 'not-started';
this.zkSyncCheckoutStep3Status = 'not-started';
this.showZkSyncModal = false;
throw new Error(`Insufficient ${tokenSymbol} balance to complete checkout`, 'error');
}
}

// Check ETH
const userEthBalance = await web3.eth.getBalance(this.userAddress);

if (BigNumber.from(userEthBalance).lt(BigNumber.from(overrides.value))) {
this.zkSyncCheckoutStep1Status = 'not-started';
this.zkSyncCheckoutStep2Status = 'not-started';
this.zkSyncCheckoutStep3Status = 'not-started';
this.showZkSyncModal = false;
throw new Error('Insufficient ETH balance to complete checkout');
}


// Send transaction
console.log('Waiting for user to send deposit transaction...');
indicateMetamaskPopup();
depositTx = await batckZkSyncDepositContract.deposit(depositRecipient, deposits, overrides);

} else if (selectedTokens.length === 1 && selectedTokens[0] === 'ETH') {
// If only ETH deposit ---------------------------------------------------
// Check ETH balance
const userEthBalance = await web3.eth.getBalance(this.userAddress);

if (BigNumber.from(userEthBalance).lt(BigNumber.from(overrides.value))) {
this.zkSyncCheckoutStep1Status = 'not-started';
this.zkSyncCheckoutStep2Status = 'not-started';
this.zkSyncCheckoutStep3Status = 'not-started';
this.showZkSyncModal = false;
throw new Error('Insufficient ETH balance to complete checkout');
}

// Deposit ETH
const zkSyncContract = new ethers.Contract(this.depositContractToUse, zkSyncContractAbi, this.signer);

Expand All @@ -2048,11 +2111,23 @@ Vue.component('grants-cart', {
depositTx = await zkSyncContract.depositETH(depositRecipient, overrides);

} else if (selectedTokens.length === 1 && selectedTokens[0] !== 'ETH') {
// If only token deposit ---------------------------------------------------
// Deposit tokens
const zkSyncContract = new ethers.Contract(this.depositContractToUse, zkSyncContractAbi, this.signer);
const tokenDonation = this.zkSyncSummaryData()[0];
const tokenAmount = await this.getTotalAmountToTransfer(tokenDonation.tokenName, tokenDonation.allowance);

const tokenContract = new web3.eth.Contract(token_abi, tokenDonation.contract._address);
const userTokenBalance = await tokenContract.methods.balanceOf(this.userAddress).call({from: this.userAddress});

if (BigNumber.from(userTokenBalance).lt(BigNumber.from(tokenAmount))) {
this.zkSyncCheckoutStep1Status = 'not-started';
this.zkSyncCheckoutStep2Status = 'not-started';
this.zkSyncCheckoutStep3Status = 'not-started';
this.showZkSyncModal = false;
throw new Error(`Insufficient ${tokenDonation.tokenName} balance to complete checkout`, 'error');
}

console.log('Waiting for user to send deposit transaction...');
indicateMetamaskPopup();
depositTx = await zkSyncContract.depositERC20(tokenDonation.contract._address, tokenAmount, depositRecipient, overrides);
Expand Down Expand Up @@ -2191,7 +2266,9 @@ Vue.component('grants-cart', {
}

// Update suggested checkout option
await this.checkZkSyncBalances();
if (this.zkSyncWalletState) {
await this.checkZkSyncBalances();
}
},
deep: true
},
Expand Down Expand Up @@ -2225,6 +2302,19 @@ Vue.component('grants-cart', {

// Show loading dialog
this.isLoading = true;

// Load list of all tokens
const tokensResponse = await fetch('/api/v1/tokens');
const allTokens = await tokensResponse.json();

// Only keep the ones for the current network
this.currentTokens = allTokens.filter((token) => token.network === document.web3network || 'mainnet');
this.currentTokens.forEach((token) => {
// Add addr and name fields for backwards compatibility with existing code in this file
token.addr = token.address;
token.name = token.symbol;
});

// Read array of grants in cart from localStorage
this.grantData = CartData.loadCart();
// Initialize array of empty comments
Expand Down