Skip to content

Commit

Permalink
Merge branch 'master' into tooltip-annoucement-info
Browse files Browse the repository at this point in the history
  • Loading branch information
jferas authored Jul 17, 2024
2 parents a2764fd + f1c3f69 commit ff71f90
Show file tree
Hide file tree
Showing 26 changed files with 511 additions and 123 deletions.
1 change: 0 additions & 1 deletion .github/workflows/ci.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,6 @@ env:
GNOSIS_CHAIN_RPC_URL: ${{ secrets.GNOSIS_CHAIN_RPC_URL }}
BASE_RPC_URL: $${{ secrets.BASE_RPC_URL }}
FOUNDRY_PROFILE: ci
INFURA_ID: ${{ secrets.INFURA_ID }}
WALLET_CONNECT_PROJECT_ID: ${{ secrets.WALLET_CONNECT_PROJECT_ID }}

jobs:
Expand Down
1 change: 0 additions & 1 deletion contracts-core/.env.example
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
INFURA_ID=zzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzz
MNEMONIC=here is where your twelve words mnemonic should be put my friend
DEPLOY_GSN=false
ETHERSCAN_VERIFICATION_API_KEY="YOUR_API_KEY"
Expand Down
2 changes: 1 addition & 1 deletion contracts-periphery/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -13,4 +13,4 @@ install :; $(INSTALL_CMD)
test :; forge test --sender 0x4f78F7f3482D9f1790649f9DD18Eec5A1Cc70F86 --no-match-contract ApproveBatchSendTokensTest
test-gas :; forge test --match-path *.gas.t.sol
snapshot-gas :; forge test --match-path *.gas.t.sol --gas-report > snapshot/.gas
coverage :; forge coverage --report lcov --report summary && sed -i'.bak' 's/SF:/SF:contracts-periphery\//gI' lcov.info
coverage :; forge coverage --sender 0x4f78F7f3482D9f1790649f9DD18Eec5A1Cc70F86 --report lcov --report summary && sed -i'.bak' 's/SF:/SF:contracts-periphery\//gI' lcov.info
3 changes: 2 additions & 1 deletion contracts-periphery/script/ApproveBatchSendTokens.s.sol
Original file line number Diff line number Diff line change
Expand Up @@ -7,11 +7,12 @@ import {UmbraBatchSend} from "src/UmbraBatchSend.sol";

contract ApproveBatchSendTokens is Script {
function run(
address _owner,
address _umbraContractAddress,
address _batchSendContractAddress,
address[] calldata _tokenAddressesToApprove
) public {
vm.startBroadcast();
vm.startBroadcast(_owner);
for (uint256 _i = 0; _i < _tokenAddressesToApprove.length; _i++) {
uint256 _currentAllowance = IERC20(_tokenAddressesToApprove[_i]).allowance(
_batchSendContractAddress, _umbraContractAddress
Expand Down
2 changes: 1 addition & 1 deletion contracts-periphery/script/DeployBatchSend.s.sol
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ contract DeployBatchSend is Script {
/// @notice Deploy the contract to the list of networks,
function run() public {
// Compute the address the contract will be deployed to
address expectedContractAddress = computeCreateAddress(msg.sender, EXPECTED_NONCE);
address expectedContractAddress = vm.computeCreateAddress(msg.sender, EXPECTED_NONCE);
console2.log("Expected contract address: %s", expectedContractAddress);

// Turn off fallback to default RPC URLs since they can be flaky.
Expand Down
8 changes: 6 additions & 2 deletions contracts-periphery/test/ApproveBatchSendTokens.t.sol
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ contract ApproveBatchSendTokensTest is Test {
address constant WBTC_ADDRESS = 0x2260FAC5E5542a773Aa44fBCfeDf7C193bc2C599;
address[] tokensToApprove =
[DAI_ADDRESS, LUSD_ADDRESS, RAI_ADDRESS, USDC_ADDRESS, USDT_ADDRESS, WBTC_ADDRESS];
address owner = 0xB7EE870E2c49B2DEEe70003519cF056247Aac3D4;

function setUp() public {
vm.createSelectFork(vm.rpcUrl("mainnet"), 18_428_858);
Expand All @@ -27,7 +28,10 @@ contract ApproveBatchSendTokensTest is Test {
address[] memory tokenAddressesToApprove = new address[](1);
tokenAddressesToApprove[0] = DAI_ADDRESS;
approveTokensScript.run(
umbraContractAddressOnMainnet, batchSendContractAddressOnMainnet, tokenAddressesToApprove
owner,
umbraContractAddressOnMainnet,
batchSendContractAddressOnMainnet,
tokenAddressesToApprove
);

assertEq(
Expand All @@ -40,7 +44,7 @@ contract ApproveBatchSendTokensTest is Test {

function test_ApproveMultipleTokens() public {
approveTokensScript.run(
umbraContractAddressOnMainnet, batchSendContractAddressOnMainnet, tokensToApprove
owner, umbraContractAddressOnMainnet, batchSendContractAddressOnMainnet, tokensToApprove
);

for (uint256 _i; _i < tokensToApprove.length; _i++) {
Expand Down
2 changes: 1 addition & 1 deletion contracts-periphery/test/DeployBatchSend.t.sol
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ contract DeployBatchSendTest is DeployBatchSend, Test {
bytes batchSendCode;

function setUp() public {
expectedContractAddress = computeCreateAddress(sender, EXPECTED_NONCE);
expectedContractAddress = vm.computeCreateAddress(sender, EXPECTED_NONCE);
umbraBatchSendTest = new UmbraBatchSend(IUmbra(UMBRA));
batchSendCode = address(umbraBatchSendTest).code;
}
Expand Down
14 changes: 14 additions & 0 deletions contracts-periphery/test/UmbraBatchSend.t.sol
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,18 @@ abstract contract UmbraBatchSendTest is DeployUmbraTest {
error NotSorted();
error TooMuchEthSent();

function _sortSendDataByToken(UmbraBatchSend.SendData[] storage arr) internal {
for (uint256 i = 0; i < arr.length - 1; i++) {
for (uint256 j = 0; j < arr.length - i - 1; j++) {
if (arr[j].tokenAddr > arr[j + 1].tokenAddr) {
UmbraBatchSend.SendData memory temp = arr[j];
arr[j] = arr[j + 1];
arr[j + 1] = temp;
}
}
}
}

function setUp() public virtual override {
super.setUp();
router = new UmbraBatchSend(IUmbra(address(umbra)));
Expand Down Expand Up @@ -94,6 +106,8 @@ abstract contract UmbraBatchSendTest is DeployUmbraTest {
sendData.push(UmbraBatchSend.SendData(alice, address(token), amount, pkx, ciphertext));
sendData.push(UmbraBatchSend.SendData(bob, address(token), amount2, pkx, ciphertext));

_sortSendDataByToken(sendData);

uint256 totalToll = toll * sendData.length;
token.approve(address(router), totalAmount);
token2.approve(address(router), totalAmount2);
Expand Down
1 change: 0 additions & 1 deletion frontend/.env.example
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@ OPTIMISTIC_ETHERSCAN_API_KEY=yourOptimisticEtherscanApiKey
POLYGONSCAN_API_KEY=yourPolygonscanApiKey
ARBISCAN_API_KEY=yourArbiscanApiKey

INFURA_ID=yourKeyHere
BLOCKNATIVE_API_KEY=yourKeyHere
FORTMATIC_API_KEY=yourKeyHere
PORTIS_API_KEY=yourKeyHere
Expand Down
1 change: 1 addition & 0 deletions frontend/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
"@metamask/jazzicon": "^2.0.0",
"@quasar/extras": "^1.15.8",
"@umbracash/umbra-js": "file:../umbra-js",
"@umbracash/umbra-js": "0.2.1",
"@uniswap/token-lists": "^1.0.0-beta.19",
"@unstoppabledomains/resolution": "8.5.0",
"@web3-onboard/coinbase": "2.2.7",
Expand Down
96 changes: 82 additions & 14 deletions frontend/src/components/AccountReceiveTable.vue
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,10 @@
>.
</div>

<div v-if="scanStatus === 'fetching' || scanStatus === 'complete'">
<div v-if="mostRecentAnnouncementBlockNumber && mostRecentAnnouncementTimestamp" class="text-caption q-mb-md">
<!-- Container for block data and fetching status -->
<div class="block-data-container row items-center justify-between q-col-gutter-md">
<!-- Block data -->
<span class="q-mr-xs">{{ $t('AccountReceiveTable.funds-question') }}</span>
<base-tooltip icon="fas fa-question-circle">
<span class="text-bold q-mb-sm">
Expand Down Expand Up @@ -90,9 +93,40 @@
{{ $t('AccountReceiveTable.learn-more') }}
</router-link>
</base-tooltip>
</div>
<div v-if="scanStatus === 'complete'" class="text-caption q-mb-sm">
<!-- Show the most recent timestamp and block that were scanned -->
<!-- Status messages -->
<div
v-if="
['fetching', 'fetching latest', 'scanning', 'scanning latest from last fetched block'].includes(
scanStatus
)
"
class="status-message text-italic"
>
<div v-if="scanStatus === 'fetching' || scanStatus === 'fetching latest'">
{{
scanStatus === 'fetching'
? $t('Receive.fetching')
: $t('Receive.fetching-latest-from-last-fetched-block')
}}
<q-spinner-dots color="primary" size="1em" class="q-ml-xs" />
</div>
<div v-else>
{{
scanStatus === 'scanning latest from last fetched block'
? $t('Receive.scanning-latest-from-last-fetched-block')
: $t('Receive.scanning')
}}
<q-spinner-dots color="primary" size="1em" class="q-ml-xs" />
</div>
</div>
</div>

<div v-if="advancedMode" class="text-caption q-mb-sm">
{{ $t('AccountReceiveTable.most-recent-mined') }}
{{ mostRecentBlockNumber }} /
{{ formatDate(mostRecentBlockTimestamp * 1000) }}
{{ formatTime(mostRecentBlockTimestamp * 1000) }}
</div>
<div v-if="advancedMode" class="text-caption q-mb-sm">
<!-- This scanDescriptionString describes scan settings that were used -->
{{ scanDescriptionString }}.
Expand Down Expand Up @@ -406,7 +440,7 @@
</template>

<script lang="ts">
import { computed, defineComponent, watch, PropType, ref, watchEffect, Ref } from 'vue';
import { computed, defineComponent, watch, PropType, ref, watchEffect, Ref, ComputedRef, onMounted } from 'vue';
import { copyToClipboard } from 'quasar';
import { BigNumber, Contract, joinSignature, formatUnits, TransactionResponse, Web3Provider } from 'src/utils/ethers';
import { Umbra, UserAnnouncement, KeyPair, utils } from '@umbracash/umbra-js';
Expand Down Expand Up @@ -482,7 +516,7 @@ interface ReceiveTableAnnouncement extends UserAnnouncement {
formattedFrom: string;
}
function useReceivedFundsTable(userAnnouncements: Ref<UserAnnouncement[]>, spendingKeyPair: KeyPair) {
function useReceivedFundsTable(userAnnouncements: Ref<UserAnnouncement[]>, spendingKeyPair: ComputedRef<KeyPair>) {
const { NATIVE_TOKEN, network, provider, signer, umbra, userAddress, relayer, tokens } = useWalletStore();
const { setIsInWithdrawFlow } = useStatusesStore();
const paginationConfig = { rowsPerPage: 25 };
Expand Down Expand Up @@ -563,13 +597,17 @@ function useReceivedFundsTable(userAnnouncements: Ref<UserAnnouncement[]>, spend
// Format announcements so from addresses support ENS/CNS, and so we can easily detect withdrawals
const formattedAnnouncements = ref([] as ReceiveTableAnnouncement[]);
const sortByTimestamp = (announcements: ReceiveTableAnnouncement[]) =>
announcements.sort((a, b) => Number(b.timestamp) - Number(a.timestamp));
// eslint-disable-next-line @typescript-eslint/no-misused-promises
watchEffect(async () => {
if (userAnnouncements.value.length === 0) formattedAnnouncements.value = [];
isLoading.value = true;
const hasAnnouncements = userAnnouncements.value.length > 0;
if (!hasAnnouncements) formattedAnnouncements.value = [];
isLoading.value = !hasAnnouncements;
const announcements = userAnnouncements.value as ReceiveTableAnnouncement[];
const newAnnouncements = announcements.filter((x) => !formattedAnnouncements.value.includes(x));
formattedAnnouncements.value = [...formattedAnnouncements.value, ...newAnnouncements];
formattedAnnouncements.value = sortByTimestamp([...formattedAnnouncements.value, ...newAnnouncements]);
// Format addresses to use ENS, CNS, or formatted address
const fromAddresses = announcements.map((announcement) => announcement.from);
let formattedAddresses: string[] = [];
Expand Down Expand Up @@ -605,9 +643,19 @@ function useReceivedFundsTable(userAnnouncements: Ref<UserAnnouncement[]>, spend
const stealthBalanceResponses: Response[] = await multicall.callStatic.aggregate3(stealthBalanceCalls);
const stealthBalances = stealthBalanceResponses.map((r) => BigNumber.from(r.returnData));
formattedAnnouncements.value.forEach((announcement, index) => {
if (newAnnouncements.some((newAnnouncement) => newAnnouncement.txHash === announcement.txHash))
announcement.isWithdrawn = stealthBalances[index].lt(announcement.amount);
formattedAnnouncements.value.forEach((announcement) => {
const isNewAnnouncement = newAnnouncements.some(
(newAnnouncement) =>
newAnnouncement.txHash === announcement.txHash && newAnnouncement.receiver === announcement.receiver
);
if (isNewAnnouncement) {
const balanceIndex = userAnnouncements.value.findIndex(
(a) => a.txHash === announcement.txHash && a.receiver === announcement.receiver
);
const stealthBalance = stealthBalances[balanceIndex];
announcement.isWithdrawn = stealthBalance.lt(announcement.amount);
}
});
isLoading.value = false;
});
Expand Down Expand Up @@ -697,7 +745,7 @@ function useReceivedFundsTable(userAnnouncements: Ref<UserAnnouncement[]>, spend
// Get token info, stealth private key, and destination (acceptor) address
const announcement = activeAnnouncement.value;
const token = getTokenInfo(announcement.token);
const stealthKeyPair = spendingKeyPair.mulPrivateKey(announcement.randomNumber);
const stealthKeyPair = spendingKeyPair.value.mulPrivateKey(announcement.randomNumber);
const spendingPrivateKey = stealthKeyPair.privateKeyHex as string;
const acceptor = await toAddress(destinationAddress.value, provider.value);
await utils.assertSupportedAddress(acceptor);
Expand Down Expand Up @@ -858,6 +906,10 @@ export default defineComponent({
}
);
onMounted(() => {
setIsInWithdrawFlow(false);
});
return {
advancedMode,
context,
Expand All @@ -869,7 +921,7 @@ export default defineComponent({
userAnnouncements,
setIsInWithdrawFlow,
...useAdvancedFeatures(spendingKeyPair.value),
...useReceivedFundsTable(userAnnouncements, spendingKeyPair.value),
...useReceivedFundsTable(userAnnouncements, spendingKeyPair),
};
},
});
Expand All @@ -890,4 +942,20 @@ export default defineComponent({
.external-link-icon
color: transparent
.block-data-container
@media (max-width: 599px)
flex-direction: column
align-items: flex-start
@media (min-width: 600px)
flex-direction: row
.block-data, .fetching-status
@media (max-width: 599px)
width: 100%
.fetching-status
@media (max-width: 599px)
margin-top: 0.5rem
</style>
56 changes: 44 additions & 12 deletions frontend/src/components/WithdrawForm.vue
Original file line number Diff line number Diff line change
Expand Up @@ -14,17 +14,14 @@
<base-input
v-model="content"
@update:modelValue="emitUpdateDestinationAddress"
@click="
emit('initializeWithdraw');
setIsInWithdrawFlow(true);
"
:appendButtonLabel="$t('WithdrawForm.withdraw')"
:appendButtonDisable="isInWithdrawFlow || isFeeLoading"
@click="handleSubmit"
:appendButtonLabel="needSignature ? $t('WithdrawForm.need-signature') : $t('WithdrawForm.withdraw')"
:appendButtonDisable="isInWithdrawFlow || isFeeLoading || isSigningInProgress"
:appendButtonLoading="isInWithdrawFlow"
:disable="isInWithdrawFlow"
:label="$t('WithdrawForm.address')"
lazy-rules
:rules="(val) => (val && val.length > 4) || $t('WithdrawForm.enter-valid-address')"
:rules="(val: string | null) => (val && val.length > 4) || $t('WithdrawForm.enter-valid-address')"
/>
<!-- Fee estimate -->
<div class="q-mb-lg">
Expand Down Expand Up @@ -119,26 +116,61 @@ export default defineComponent({
advancedMode: {
type: Boolean,
required: true,
default: true,
},
},
setup(data, { emit }) {
const { NATIVE_TOKEN } = useWalletStore();
const { NATIVE_TOKEN, getPrivateKeys } = useWalletStore();
const { setIsInWithdrawFlow, isInWithdrawFlow } = useStatusesStore();
const { needSignature } = useWalletStore();
const content = ref<string>(data.destinationAddress || '');
const nativeTokenSymbol = NATIVE_TOKEN.value.symbol;
const isSigningInProgress = ref(false);
function emitUpdateDestinationAddress(val: string) {
emit('updateDestinationAddress', val);
}
function initializeWithdraw() {
// Simple validation
if (!content.value || content.value.length <= 4) return;
emit('initializeWithdraw');
setIsInWithdrawFlow(true);
}
async function handleSubmit() {
if (needSignature.value) {
try {
isSigningInProgress.value = true;
const success = await getPrivateKeys();
if (success === 'denied') {
console.log('User denied signature request');
isSigningInProgress.value = false;
return;
}
initializeWithdraw();
} catch (error) {
console.error('Error getting private keys:', error);
} finally {
isSigningInProgress.value = false;
}
} else {
initializeWithdraw();
}
}
return {
formatUnits,
humanizeTokenAmount,
content,
emit,
emitUpdateDestinationAddress,
content,
nativeTokenSymbol,
formatUnits,
handleSubmit,
humanizeTokenAmount,
isInWithdrawFlow,
isSigningInProgress,
nativeTokenSymbol,
needSignature,
setIsInWithdrawFlow,
};
},
Expand Down
Loading

0 comments on commit ff71f90

Please sign in to comment.