Merge pull request #41 from liftlearning/withdraw

Withdraw
This commit is contained in:
Rafael Ramos 2023-02-15 00:54:07 -03:00 committed by GitHub
commit dab0842b65
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
12 changed files with 200 additions and 206 deletions

View File

@ -1,7 +1,7 @@
import { useEtherStore } from "@/store/ether"; import { useEtherStore } from "@/store/ether";
import { getContract, getProvider } from "./provider"; import { getContract, getProvider } from "./provider";
import { getP2PixAddress } from "./addresses"; import { getP2PixAddress, getTokenAddress } from "./addresses";
import p2pix from "../utils/smart_contract_files/P2PIX.json"; import p2pix from "../utils/smart_contract_files/P2PIX.json";
@ -84,15 +84,18 @@ const cancelDeposit = async (depositId: BigNumber): Promise<any> => {
return cancel; return cancel;
}; };
const withdrawDeposit = async ( const withdrawDeposit = async (amount: string): Promise<any> => {
depositId: BigNumber,
amount: string
): Promise<any> => {
const contract = getContract(); const contract = getContract();
const withdraw = await contract.withdraw(depositId, amount, []); const withdraw = await contract.withdraw(
await withdraw.wait(); getTokenAddress(),
parseEther(String(amount)),
[]
);
const with_rec = await withdraw.wait();
const [t] = with_rec.events;
console.log(t.args);
return withdraw; return withdraw;
}; };

View File

@ -64,11 +64,21 @@ const listAllTransactionByWalletAddress = async (
filterReleasedLocks filterReleasedLocks
); );
return [...eventsDeposits, ...eventsAddedLocks, ...eventsReleasedLocks].sort( const filterWithdrawnDeposits = p2pContract.filters.DepositWithdrawn([
(a, b) => { walletAddress,
return b.blockNumber - a.blockNumber; ]);
} const eventsWithdrawnDeposits = await p2pContract.queryFilter(
filterWithdrawnDeposits
); );
return [
...eventsDeposits,
...eventsAddedLocks,
...eventsReleasedLocks,
...eventsWithdrawnDeposits,
].sort((a, b) => {
return b.blockNumber - a.blockNumber;
});
}; };
// get wallet's release transactions // get wallet's release transactions

View File

@ -56,6 +56,7 @@ const emit = defineEmits(["makeAnotherTransaction"]);
</div> </div>
<div class="w-full max-w-4xl lg-view"> <div class="w-full max-w-4xl lg-view">
<ListingComponent <ListingComponent
:valid-deposits="[]"
:walletTransactions="lastWalletReleaseTransactions" :walletTransactions="lastWalletReleaseTransactions"
:isManageMode="false" :isManageMode="false"
> >

View File

@ -1,4 +1,5 @@
<script setup lang="ts"> <script setup lang="ts">
import { withdrawDeposit } from "@/blockchain/buyerMethods";
import { NetworkEnum } from "@/model/NetworkEnum"; import { NetworkEnum } from "@/model/NetworkEnum";
import type { ValidDeposit } from "@/model/ValidDeposit"; import type { ValidDeposit } from "@/model/ValidDeposit";
import { useEtherStore } from "@/store/ether"; import { useEtherStore } from "@/store/ether";
@ -8,13 +9,37 @@ import { ref, watch } from "vue";
// props // props
const props = defineProps<{ const props = defineProps<{
walletTransactions: (Event | ValidDeposit)[]; validDeposits: ValidDeposit[];
isManageMode: boolean; walletTransactions: Event[];
}>(); }>();
const emit = defineEmits(["depositWithdrawn"]);
const etherStore = useEtherStore(); const etherStore = useEtherStore();
const itemsToShow = ref<(Event | ValidDeposit)[]>([]); const itemsToShow = ref<Event[]>([]);
const withdrawAmount = ref<string>("");
const callWithdraw = async () => {
if (withdrawAmount.value) {
const withdraw = await withdrawDeposit(withdrawAmount.value);
if (withdraw) {
console.log(withdraw);
alert("Saque realizado!");
emit("depositWithdrawn");
}
}
};
const getRemaining = (): number => {
if (props.validDeposits instanceof Array) {
// Here we are getting only the first element of the list because
// in this release only the BRL token is being used.
const deposit = props.validDeposits[0];
return deposit ? deposit.remaining : 0;
}
return 0;
};
const getExplorer = (): string => { const getExplorer = (): string => {
return etherStore.networkName == NetworkEnum.ethereum return etherStore.networkName == NetworkEnum.ethereum
@ -22,13 +47,6 @@ const getExplorer = (): string => {
: "Polygonscan"; : "Polygonscan";
}; };
// Methods
const isValidDeposit = (
deposit: Event | ValidDeposit
): deposit is ValidDeposit => {
return (deposit as ValidDeposit).token !== undefined;
};
const showInitialItems = (): void => { const showInitialItems = (): void => {
itemsToShow.value = props.walletTransactions.slice(0, 3); itemsToShow.value = props.walletTransactions.slice(0, 3);
}; };
@ -56,6 +74,7 @@ const getEventName = (event: string | undefined): string => {
DepositAdded: "Oferta", DepositAdded: "Oferta",
LockAdded: "Reserva", LockAdded: "Reserva",
LockReleased: "Compra", LockReleased: "Compra",
DepositWithdrawn: "Retirada",
}; };
return possibleEventName[event]; return possibleEventName[event];
@ -77,62 +96,41 @@ watch(props, async (): Promise<void> => {
: props.walletTransactions; : props.walletTransactions;
}); });
//emits
const emit = defineEmits(["withdrawDeposit"]);
// initial itemsToShow valueb // initial itemsToShow valueb
showInitialItems(); showInitialItems();
</script> </script>
<template> <template>
<div class="blur-container"> <div class="blur-container">
<div <div class="w-full bg-white p-6 rounded-lg">
class="w-full bg-white p-6 rounded-lg"
v-for="(item, index) in itemsToShow"
:key="item.blockNumber"
>
<div class="flex justify-between items-center"> <div class="flex justify-between items-center">
<div> <div>
<p class="text-sm leading-5 font-medium text-gray-600"> <p class="text-sm leading-5 font-medium text-gray-600">
{{ getEventName((item as Event).event) }} Saldo disponível
</p> </p>
<p class="text-xl leading-7 font-semibold text-gray-900"> <p class="text-xl leading-7 font-semibold text-gray-900">
{{ {{ getRemaining() }} BRZ
isValidDeposit(item)
? item.remaining
: getAmountFormatted(item.args?.amount)
}}
BRZ
</p> </p>
<p class="text-xs leading-4 font-medium text-gray-600">20/08/2022</p> <p class="text-xs leading-4 font-medium text-gray-600"></p>
</div>
<div>
<div class="bg-emerald-300 rounded-lg text-center mb-2">
Finalizado
</div>
<div
v-if="!props.isManageMode"
class="flex gap-2 cursor-pointer items-center justify-self-center"
@click="openEtherscanUrl((item as Event)?.transactionHash)"
>
<span class="last-release-info">{{ getExplorer() }}</span>
<img alt="Redirect image" src="@/assets/redirect.svg" />
</div>
</div> </div>
</div> </div>
<div class="pt-5" v-if="props.isManageMode"> <div class="pt-5">
<!-- <div class="py-2"> <div class="py-2 w-100">
<p class="text-sm leading-5 font-medium">Valor do saque</p> <p class="text-sm leading-5 font-medium">Valor do saque</p>
<p class="text-2xl leading-8 font-medium">0</p> <input
</div> --> type="number"
name=""
id=""
placeholder="0"
class="text-2xl text-gray-900 w-full outline-none"
v-model="withdrawAmount"
/>
</div>
<hr class="pb-3" /> <hr class="pb-3" />
<div class="flex justify-between items-center"> <div class="flex justify-end items-center">
<div <div
class="flex gap-2 cursor-pointer items-center justify-self-center border-2 p-2 border-amber-300 rounded-md" class="flex gap-2 cursor-pointer items-center justify-self-center border-2 p-2 border-amber-300 rounded-md"
@click=" @click="callWithdraw"
emit('withdrawDeposit', (item as ValidDeposit).token, index)
"
> >
<img alt="Withdraw image" src="@/assets/withdraw.svg" /> <img alt="Withdraw image" src="@/assets/withdraw.svg" />
<span class="last-release-info">Sacar</span> <span class="last-release-info">Sacar</span>
@ -140,6 +138,36 @@ showInitialItems();
</div> </div>
</div> </div>
</div> </div>
<div
class="w-full bg-white p-6 rounded-lg"
v-for="item in itemsToShow"
:key="item.blockNumber"
>
<div class="item-container">
<div>
<p class="text-sm leading-5 font-medium text-gray-600">
{{ getEventName(item.event) }}
</p>
<p class="text-xl leading-7 font-semibold text-gray-900">
{{ getAmountFormatted(item.args?.amount) }}
BRZ
</p>
<p class="text-xs leading-4 font-medium text-gray-600"></p>
</div>
<div>
<div class="bg-emerald-300 rounded-lg text-center mb-2 p-1">
Finalizado
</div>
<div
class="flex gap-2 cursor-pointer items-center justify-self-center"
@click="openEtherscanUrl(item?.transactionHash)"
>
<span class="last-release-info">{{ getExplorer() }}</span>
<img alt="Redirect image" src="@/assets/redirect.svg" />
</div>
</div>
</div>
</div>
<div <div
class="flex flex-col justify-center items-center w-full mt-2 gap-2" class="flex flex-col justify-center items-center w-full mt-2 gap-2"
v-if=" v-if="
@ -156,7 +184,7 @@ showInitialItems();
</button> </button>
<span class="text-gray-300"> <span class="text-gray-300">
({{ itemsToShow.length }} de {{ props.walletTransactions.length }} ({{ itemsToShow.length }} de {{ props.walletTransactions.length }}
{{ isManageMode ? "ofertas" : "transações" }}) transações )
</span> </span>
</div> </div>
@ -179,6 +207,10 @@ p {
@apply flex flex-col items-center justify-center gap-4; @apply flex flex-col items-center justify-center gap-4;
} }
.item-container {
@apply flex justify-between items-center;
}
.text { .text {
@apply text-white text-center; @apply text-white text-center;
} }

View File

@ -13,50 +13,23 @@ describe("ListingComponent.vue", () => {
test("Test Message when an empty array is received", () => { test("Test Message when an empty array is received", () => {
const wrapper = mount(ListingComponent, { const wrapper = mount(ListingComponent, {
props: { props: {
validDeposits: [],
walletTransactions: [], walletTransactions: [],
isManageMode: true,
}, },
}); });
expect(wrapper.html()).toContain("Não há nenhuma transação anterior"); expect(wrapper.html()).toContain("Não há nenhuma transação anterior");
}); });
test("Test Headers on List in Manage Mode", () => {
const wrapper = mount(ListingComponent, {
props: {
walletTransactions: MockValidDeposits,
isManageMode: true,
},
});
expect(wrapper.html()).toContain("Valor");
expect(wrapper.html()).toContain("Data");
expect(wrapper.html()).toContain("Retirar tokens");
});
test("Test Headers on List in Unmanage Mode", () => {
const wrapper = mount(ListingComponent, {
props: {
walletTransactions: MockEvents,
isManageMode: false,
},
});
expect(wrapper.html()).toContain("Valor");
expect(wrapper.html()).toContain("Data");
expect(wrapper.html()).toContain("Tipo de transação");
expect(wrapper.html()).toContain("Checar transação");
});
test("Test number of elements in the list first render", () => { test("Test number of elements in the list first render", () => {
const wrapper = mount(ListingComponent, { const wrapper = mount(ListingComponent, {
props: { props: {
validDeposits: [],
walletTransactions: MockEvents, walletTransactions: MockEvents,
isManageMode: false,
}, },
}); });
const elements = wrapper.findAll(".transaction-date"); const elements = wrapper.findAll(".item-container");
expect(elements).toHaveLength(3); expect(elements).toHaveLength(3);
}); });
@ -64,33 +37,33 @@ describe("ListingComponent.vue", () => {
test("Test load more button behavior", async () => { test("Test load more button behavior", async () => {
const wrapper = mount(ListingComponent, { const wrapper = mount(ListingComponent, {
props: { props: {
walletTransactions: MockValidDeposits, validDeposits: MockValidDeposits,
isManageMode: false, walletTransactions: MockEvents,
}, },
}); });
const btn = wrapper.find("button"); const btn = wrapper.find("button");
let elements = wrapper.findAll(".transaction-date"); let elements = wrapper.findAll(".item-container");
expect(elements).toHaveLength(3); expect(elements).toHaveLength(3);
await btn.trigger("click"); await btn.trigger("click");
elements = wrapper.findAll(".transaction-date"); elements = wrapper.findAll(".item-container");
expect(elements).toHaveLength(5); expect(elements).toHaveLength(4);
}); });
test("Test withdraw offer button emit", async () => { test("Test withdraw offer button emit", async () => {
const wrapper = mount(ListingComponent, { const wrapper = mount(ListingComponent, {
props: { props: {
walletTransactions: MockValidDeposits, validDeposits: MockValidDeposits,
isManageMode: true, walletTransactions: MockEvents,
}, },
}); });
wrapper.vm.$emit("withdrawDeposit"); wrapper.vm.$emit("depositWithdrawn");
await wrapper.vm.$nextTick(); await wrapper.vm.$nextTick();
expect(wrapper.emitted("withdrawDeposit")).toBeTruthy(); expect(wrapper.emitted("depositWithdrawn")).toBeTruthy();
}); });
}); });

View File

@ -56,8 +56,9 @@ const handleButtonClick = async (
offer: string, offer: string,
pixKey: string pixKey: string
): Promise<void> => { ): Promise<void> => {
console.log(postProcessKey(pixKey)); const postProcessedPixKey = postProcessKey(pixKey);
if (walletAddress.value) emit("approveTokens", { offer, pixKey }); if (walletAddress.value)
emit("approveTokens", { offer, postProcessedPixKey });
else await connectProvider(); else await connectProvider();
}; };
</script> </script>

View File

@ -284,14 +284,6 @@ onClickOutside(currencyRef, () => {
> >
<div class="pl-4 mt-2"> <div class="pl-4 mt-2">
<div class="bg-white rounded-md z-10"> <div class="bg-white rounded-md z-10">
<div class="menu-button" @click="closeMenu()">
<RouterLink to="/transaction_history" class="redirect_button">
Histórico de transações
</RouterLink>
</div>
<div class="w-full flex justify-center">
<hr class="w-4/5" />
</div>
<div class="menu-button" @click="closeMenu()"> <div class="menu-button" @click="closeMenu()">
<RouterLink to="/manage_bids" class="redirect_button"> <RouterLink to="/manage_bids" class="redirect_button">
Gerenciar Ofertas Gerenciar Ofertas
@ -328,14 +320,6 @@ onClickOutside(currencyRef, () => {
<div class="w-full flex justify-center"> <div class="w-full flex justify-center">
<hr class="w-4/5" /> <hr class="w-4/5" />
</div> </div>
<div class="menu-button" @click="closeMenu()">
<RouterLink to="/transaction_history" class="redirect_button">
Histórico de transações
</RouterLink>
</div>
<div class="w-full flex justify-center">
<hr class="w-4/5" />
</div>
<div class="menu-button" @click="closeMenu()"> <div class="menu-button" @click="closeMenu()">
<RouterLink to="/manage_bids" class="redirect_button"> <RouterLink to="/manage_bids" class="redirect_button">
Gerenciar Ofertas Gerenciar Ofertas

View File

@ -30,6 +30,6 @@ describe("TopBar.vue", () => {
it("should render the P2Pix logo correctly", () => { it("should render the P2Pix logo correctly", () => {
const wrapper = shallowMount(TopBar); const wrapper = shallowMount(TopBar);
const img = wrapper.findAll(".logo"); const img = wrapper.findAll(".logo");
expect(img.length).toBe(1); expect(img.length).toBe(2);
}); });
}); });

View File

@ -89,4 +89,33 @@ export const MockEvents: Event[] = [
getTransaction: vi.fn(), getTransaction: vi.fn(),
getTransactionReceipt: vi.fn(), getTransactionReceipt: vi.fn(),
}, },
{
blockNumber: 4,
blockHash: "0x8",
transactionIndex: 4,
removed: false,
address: "0x0",
data: "0x0",
topics: ["0x0", "0x0"],
transactionHash: "0x0",
logIndex: 4,
event: "LockReleased",
eventSignature: "LockReleased(address,uint256,address,uint256)",
args: [
"0x0",
{
type: "BigNumber",
hex: "0x00",
},
"0x0",
{
type: "BigNumber",
hex: "0x6c6b935b8bbd400000",
},
],
getBlock: vi.fn(),
removeListener: vi.fn(),
getTransaction: vi.fn(),
getTransactionReceipt: vi.fn(),
},
]; ];

View File

@ -1,6 +1,5 @@
import { createRouter, createWebHistory } from "vue-router"; import { createRouter, createWebHistory } from "vue-router";
import HomeView from "../views/HomeView.vue"; import HomeView from "../views/HomeView.vue";
import TransactionHistoryView from "../views/TransactionHistoryView.vue";
import FaqView from "../views/FaqView.vue"; import FaqView from "../views/FaqView.vue";
import ManageBidsView from "../views/ManageBidsView.vue"; import ManageBidsView from "../views/ManageBidsView.vue";
import SellerView from "@/views/SellerView.vue"; import SellerView from "@/views/SellerView.vue";
@ -18,11 +17,6 @@ const router = createRouter({
name: "seller", name: "seller",
component: SellerView, component: SellerView,
}, },
{
path: "/transaction_history",
name: "transaction history",
component: TransactionHistoryView,
},
{ {
path: "/manage_bids", path: "/manage_bids",
name: "manage bids", name: "manage bids",

View File

@ -2,45 +2,67 @@
import { useEtherStore } from "@/store/ether"; import { useEtherStore } from "@/store/ether";
import { storeToRefs } from "pinia"; import { storeToRefs } from "pinia";
import ListingComponent from "@/components/ListingComponent/ListingComponent.vue"; import ListingComponent from "@/components/ListingComponent/ListingComponent.vue";
import type { BigNumber } from "ethers";
import { ref, watch, onMounted } from "vue"; import { ref, watch, onMounted } from "vue";
import { withdrawDeposit } from "@/blockchain/buyerMethods"; import {
import { listValidDepositTransactionsByWalletAddress } from "@/blockchain/wallet"; listValidDepositTransactionsByWalletAddress,
listAllTransactionByWalletAddress,
} from "@/blockchain/wallet";
import type { ValidDeposit } from "@/model/ValidDeposit"; import type { ValidDeposit } from "@/model/ValidDeposit";
import type { Event } from "ethers";
const etherStore = useEtherStore(); const etherStore = useEtherStore();
const { walletAddress, networkName } = storeToRefs(etherStore); const { walletAddress, networkName } = storeToRefs(etherStore);
const depositList = ref<ValidDeposit[]>([]); const depositList = ref<ValidDeposit[]>([]);
const transactionsList = ref<Event[]>([]);
const updateRemaining = async () => {
const walletDeposits = await listValidDepositTransactionsByWalletAddress(
walletAddress.value
);
depositList.value = walletDeposits;
const allUserTransactions = await listAllTransactionByWalletAddress(
walletAddress.value
);
transactionsList.value = allUserTransactions;
};
onMounted(async () => { onMounted(async () => {
if (walletAddress.value) { if (walletAddress.value) {
const walletDeposits = await listValidDepositTransactionsByWalletAddress( const walletDeposits = await listValidDepositTransactionsByWalletAddress(
walletAddress.value walletAddress.value
); );
const allUserTransactions = await listAllTransactionByWalletAddress(
walletAddress.value
);
if (walletDeposits) { if (walletDeposits) {
depositList.value = walletDeposits; depositList.value = walletDeposits;
} }
if (allUserTransactions) {
transactionsList.value = allUserTransactions;
}
} }
}); });
const handleWithDrawDeposit = async (token: BigNumber, index: number) => { watch(walletAddress, async (newValue) => {
const fixedAmount = "1"; // Need to ask user for amount await listValidDepositTransactionsByWalletAddress(newValue)
const response = await withdrawDeposit(token, fixedAmount);
if (response) {
console.log("Token retirado com sucesso.");
depositList.value.splice(index, 1);
}
};
watch(walletAddress, async () => {
await listValidDepositTransactionsByWalletAddress(walletAddress.value)
.then((res) => { .then((res) => {
if (res) depositList.value = res; if (res) depositList.value = res;
}) })
.catch(() => { .catch(() => {
depositList.value = []; depositList.value = [];
}); });
await listAllTransactionByWalletAddress(newValue)
.then((res) => {
if (res) transactionsList.value = res;
})
.catch(() => {
transactionsList.value = [];
});
}); });
watch(networkName, async () => { watch(networkName, async () => {
@ -51,17 +73,25 @@ watch(networkName, async () => {
.catch(() => { .catch(() => {
depositList.value = []; depositList.value = [];
}); });
await listAllTransactionByWalletAddress(walletAddress.value)
.then((res) => {
if (res) transactionsList.value = res;
})
.catch(() => {
transactionsList.value = [];
});
}); });
</script> </script>
<template> <template>
<div class="page"> <div class="page">
<div class="header">Gerenciar ofertas</div> <div class="header">Gerenciar Ofertas</div>
<div class="w-full max-w-4xl"> <div class="w-full max-w-4xl">
<ListingComponent <ListingComponent
:wallet-transactions="depositList" :valid-deposits="depositList"
:is-manage-mode="true" :wallet-transactions="transactionsList"
@withdraw-deposit="handleWithDrawDeposit" @deposit-withdrawn="updateRemaining"
></ListingComponent> ></ListingComponent>
</div> </div>
</div> </div>
@ -73,6 +103,6 @@ watch(networkName, async () => {
} }
.header { .header {
@apply text-3xl text-gray-900 leading-9 font-bold justify-center flex; @apply text-3xl text-white leading-9 font-bold justify-center flex;
} }
</style> </style>

View File

@ -1,63 +0,0 @@
<script setup lang="ts">
import { useEtherStore } from "@/store/ether";
import { storeToRefs } from "pinia";
import { ref, watch, onMounted } from "vue";
import ListingComponent from "@/components/ListingComponent/ListingComponent.vue";
import { listAllTransactionByWalletAddress } from "@/blockchain/wallet";
import type { Event } from "ethers";
import type { ValidDeposit } from "@/model/ValidDeposit";
const etherStore = useEtherStore();
const { walletAddress, networkName } = storeToRefs(etherStore);
const allUserTransactions = ref<(Event | ValidDeposit)[]>([]);
onMounted(async () => {
if (walletAddress.value) {
await listAllTransactionByWalletAddress(walletAddress.value).then((res) => {
if (res) allUserTransactions.value = res;
});
}
});
watch(walletAddress, async (newValue) => {
await listAllTransactionByWalletAddress(newValue)
.then((res) => {
if (res) allUserTransactions.value = res;
})
.catch(() => {
allUserTransactions.value = [];
});
});
watch(networkName, async () => {
await listAllTransactionByWalletAddress(walletAddress.value)
.then((res) => {
if (res) allUserTransactions.value = res;
})
.catch(() => {
allUserTransactions.value = [];
});
});
</script>
<template>
<div class="page">
<div class="header">Histórico de transações</div>
<div class="w-full max-w-4xl">
<ListingComponent
:wallet-transactions="allUserTransactions"
:is-manage-mode="false"
></ListingComponent>
</div>
</div>
</template>
<style scoped>
.page {
@apply flex flex-col gap-10 mt-20 w-full items-center;
}
.header {
@apply text-3xl text-white leading-9 font-bold justify-center flex;
}
</style>