diff --git a/hardhat.config.ts b/hardhat.config.ts index e48859b..1ff38f4 100644 --- a/hardhat.config.ts +++ b/hardhat.config.ts @@ -23,6 +23,7 @@ import "./tasks/core/factory/create-pair"; import "./tasks/core/demo"; import "./tasks/core/demo-router"; import "./tasks/core/demo-router-sync"; +import "./tasks/prepare/prepare-dex"; dotenv.config(); diff --git a/package-lock.json b/package-lock.json index c3d1570..f640887 100644 --- a/package-lock.json +++ b/package-lock.json @@ -12,7 +12,8 @@ "@nilfoundation/hardhat-plugin": "^0.14.0", "@nilfoundation/niljs": "^0.14.0", "@nilfoundation/smart-contracts": "^0.1.2", - "dotenv": "^16.4.5" + "dotenv": "^16.4.5", + "viem": "^2.21.28" }, "devDependencies": { "@biomejs/biome": "1.8.2", @@ -1765,6 +1766,8 @@ "version": "1.4.0", "resolved": "https://registry.npmjs.org/@scure/bip32/-/bip32-1.4.0.tgz", "integrity": "sha512-sVUpc0Vq3tXCkDGYVWGIZTRfnvu8LoTDaev7vbwh0omSvVORONr960MQWdKqJDCReIEmTj3PAr73O3aoxz7OPg==", + "dev": true, + "peer": true, "dependencies": { "@noble/curves": "~1.4.0", "@noble/hashes": "~1.4.0", @@ -1778,6 +1781,8 @@ "version": "1.4.2", "resolved": "https://registry.npmjs.org/@noble/curves/-/curves-1.4.2.tgz", "integrity": "sha512-TavHr8qycMChk8UwMld0ZDRvatedkzWfH8IiaeGCfymOP5i0hSCozz9vHOL0nkwk7HRMlFnAiKpS2jrUmSybcw==", + "dev": true, + "peer": true, "dependencies": { "@noble/hashes": "1.4.0" }, @@ -1789,6 +1794,8 @@ "version": "1.4.0", "resolved": "https://registry.npmjs.org/@noble/hashes/-/hashes-1.4.0.tgz", "integrity": "sha512-V1JJ1WTRUqHHrOSh597hURcMqVKVGL/ea3kv0gSnEdsEZ0/+VyPghM1lMNGc00z7CIQorSvbKpuJkxvuHbvdbg==", + "dev": true, + "peer": true, "engines": { "node": ">= 16" }, @@ -4777,13 +4784,13 @@ } }, "node_modules/isows": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/isows/-/isows-1.0.4.tgz", - "integrity": "sha512-hEzjY+x9u9hPmBom9IIAqdJCwNLax+xrPb51vEPpERoFlIxgmZcHzsT5jKG06nvInKOBGvReAVz80Umed5CczQ==", + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/isows/-/isows-1.0.6.tgz", + "integrity": "sha512-lPHCayd40oW98/I0uvgaHKWCSvkzY27LjWLbtzOm64yQ+G3Q5npjjbdppU65iZXkK1Zt+kH9pfegli0AYfwYYw==", "funding": [ { "type": "github", - "url": "https://github.com/sponsors/wagmi-dev" + "url": "https://github.com/sponsors/wevm" } ], "peerDependencies": { @@ -7027,9 +7034,9 @@ "devOptional": true }, "node_modules/viem": { - "version": "2.21.9", - "resolved": "https://registry.npmjs.org/viem/-/viem-2.21.9.tgz", - "integrity": "sha512-fWPDX2ABEo/mLiDN+wsmYJDJk0a/ZCafquxInR2+HZv/7UTgHbLgjZs4SotpEeFAYjgVThJ7A9TPmrRjaaYqvw==", + "version": "2.21.28", + "resolved": "https://registry.npmjs.org/viem/-/viem-2.21.28.tgz", + "integrity": "sha512-CbS2Ldq+SdZYYSG+P4oNLi1s6xq7JnZoJsIkMhFcZUMRz3w2J1OvC1izUp6E1/Zp/XXo3wt6g/Ae+f3SGzup2A==", "funding": [ { "type": "github", @@ -7037,15 +7044,15 @@ } ], "dependencies": { - "@adraffy/ens-normalize": "1.10.0", - "@noble/curves": "1.4.0", - "@noble/hashes": "1.4.0", - "@scure/bip32": "1.4.0", + "@adraffy/ens-normalize": "1.11.0", + "@noble/curves": "1.6.0", + "@noble/hashes": "1.5.0", + "@scure/bip32": "1.5.0", "@scure/bip39": "1.4.0", - "abitype": "1.0.5", - "isows": "1.0.4", - "webauthn-p256": "0.0.5", - "ws": "8.17.1" + "abitype": "1.0.6", + "isows": "1.0.6", + "webauthn-p256": "0.0.10", + "ws": "8.18.0" }, "peerDependencies": { "typescript": ">=5.0.4" @@ -7057,56 +7064,27 @@ } }, "node_modules/viem/node_modules/@adraffy/ens-normalize": { - "version": "1.10.0", - "resolved": "https://registry.npmjs.org/@adraffy/ens-normalize/-/ens-normalize-1.10.0.tgz", - "integrity": "sha512-nA9XHtlAkYfJxY7bce8DcN7eKxWWCWkU+1GR9d+U6MbNpfwQp8TI7vqOsBsMcHoT4mBu2kypKoSKnghEzOOq5Q==" + "version": "1.11.0", + "resolved": "https://registry.npmjs.org/@adraffy/ens-normalize/-/ens-normalize-1.11.0.tgz", + "integrity": "sha512-/3DDPKHqqIqxUULp8yP4zODUY1i+2xvVWsv8A79xGWdCAG+8sb0hRh0Rk2QyOJUnnbyPUAZYcpBuRe3nS2OIUg==" }, - "node_modules/viem/node_modules/@noble/curves": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/@noble/curves/-/curves-1.4.0.tgz", - "integrity": "sha512-p+4cb332SFCrReJkCYe8Xzm0OWi4Jji5jVdIZRL/PmacmDkFNw6MrrV+gGpiPxLHbV+zKFRywUWbaseT+tZRXg==", + "node_modules/viem/node_modules/@scure/bip32": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/@scure/bip32/-/bip32-1.5.0.tgz", + "integrity": "sha512-8EnFYkqEQdnkuGBVpCzKxyIwDCBLDVj3oiX0EKUFre/tOjL/Hqba1D6n/8RcmaQy4f95qQFrO2A8Sr6ybh4NRw==", "dependencies": { - "@noble/hashes": "1.4.0" - }, - "funding": { - "url": "https://paulmillr.com/funding/" - } - }, - "node_modules/viem/node_modules/@noble/hashes": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/@noble/hashes/-/hashes-1.4.0.tgz", - "integrity": "sha512-V1JJ1WTRUqHHrOSh597hURcMqVKVGL/ea3kv0gSnEdsEZ0/+VyPghM1lMNGc00z7CIQorSvbKpuJkxvuHbvdbg==", - "engines": { - "node": ">= 16" + "@noble/curves": "~1.6.0", + "@noble/hashes": "~1.5.0", + "@scure/base": "~1.1.7" }, "funding": { "url": "https://paulmillr.com/funding/" } }, - "node_modules/viem/node_modules/abitype": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/abitype/-/abitype-1.0.5.tgz", - "integrity": "sha512-YzDhti7cjlfaBhHutMaboYB21Ha3rXR9QTkNJFzYC4kC8YclaiwPBBBJY8ejFdu2wnJeZCVZSMlQJ7fi8S6hsw==", - "funding": { - "url": "https://github.com/sponsors/wevm" - }, - "peerDependencies": { - "typescript": ">=5.0.4", - "zod": "^3 >=3.22.0" - }, - "peerDependenciesMeta": { - "typescript": { - "optional": true - }, - "zod": { - "optional": true - } - } - }, "node_modules/viem/node_modules/ws": { - "version": "8.17.1", - "resolved": "https://registry.npmjs.org/ws/-/ws-8.17.1.tgz", - "integrity": "sha512-6XQFvXTkbfUOZOKKILFG1PDK2NDQs4azKQl26T0YS5CxqWLgXajbPZ+h4gZekJyRqFU8pvnbAbbs/3TgRPy+GQ==", + "version": "8.18.0", + "resolved": "https://registry.npmjs.org/ws/-/ws-8.18.0.tgz", + "integrity": "sha512-8VbfWfHLbbwu3+N6OKsOMpBdT4kXPDDB9cJk2bJ6mh9ucxdlnNvH1e+roYkKmN9Nxw2yjz7VzeO9oOz2zJ04Pw==", "engines": { "node": ">=10.0.0" }, @@ -7197,9 +7175,9 @@ } }, "node_modules/webauthn-p256": { - "version": "0.0.5", - "resolved": "https://registry.npmjs.org/webauthn-p256/-/webauthn-p256-0.0.5.tgz", - "integrity": "sha512-drMGNWKdaixZNobeORVIqq7k5DsRC9FnG201K2QjeOoQLmtSDaSsVZdkg6n5jUALJKcAG++zBPJXmv6hy0nWFg==", + "version": "0.0.10", + "resolved": "https://registry.npmjs.org/webauthn-p256/-/webauthn-p256-0.0.10.tgz", + "integrity": "sha512-EeYD+gmIT80YkSIDb2iWq0lq2zbHo1CxHlQTeJ+KkCILWpVy3zASH3ByD4bopzfk0uCwXxLqKGLqp2W4O28VFA==", "funding": [ { "type": "github", diff --git a/package.json b/package.json index ab112bd..aec3b88 100644 --- a/package.json +++ b/package.json @@ -18,7 +18,8 @@ "@nilfoundation/hardhat-plugin": "^0.14.0", "@nilfoundation/niljs": "^0.14.0", "@nilfoundation/smart-contracts": "^0.1.2", - "dotenv": "^16.4.5" + "dotenv": "^16.4.5", + "viem": "^2.21.28" }, "devDependencies": { "@biomejs/biome": "1.8.2", diff --git a/tasks/prepare/prepare-dex.ts b/tasks/prepare/prepare-dex.ts new file mode 100644 index 0000000..38c371b --- /dev/null +++ b/tasks/prepare/prepare-dex.ts @@ -0,0 +1,160 @@ +import {shardNumber} from "@nilfoundation/hardhat-plugin/dist/utils/conversion"; +import {waitTillCompleted} from "@nilfoundation/niljs"; +import {task} from "hardhat/config"; +import {encodeFunctionData} from "viem"; +import {createClient} from "../util/client"; +import {initCurrency} from "../util/currency-init"; +import {mintCurrency, sendCurrency} from "../util/currencyUtils"; +import {deployDex} from "../util/dex-deployment"; +import {initPair} from "../util/pair-init"; + +task("prepare-dex", "Prepare DEX contract and set currency to wallets") + .addParam('wallets') + .setAction(async (taskArgs, hre) => { + const walletAddress = process.env.WALLET_ADDR; + if (!walletAddress) { + throw new Error("WALLET_ADDR is not set in environment variables"); + } + + const mintAmount: number = 100000000; + const mintCurrency0Amount = 100000; + const mintCurrency1Amount = 100000; + const wallets = taskArgs.wallets.split(',') as string[]; + + const {wallet, publicClient, signer} = await createClient(); + + const {factory, routerAddress} = await deployDex(hre); + console.log("Dex deployed, router - " + routerAddress); + + const { + address: token0Address, + currency: token0Contract, + id: token0Id, + } = await initCurrency("Token0", mintAmount, hre); + const { + address: token1Address, + currency: token1Contract, + id: token1Id, + } = await initCurrency("Token1", mintAmount, hre); + + const {address: pairAddress, pair} = await initPair( + token0Address, + token1Address, + factory, + hre, + ); + console.log("Pair deployed " + pairAddress); + + console.log(`Pair initialized successfully at address: ${pairAddress}`); + + // 2. MINT CURRENCIES + console.log( + `Minting ${mintAmount} Currency0 to wallet ${walletAddress}...`, + ); + await mintCurrency({ + publicClient, + signer, + contractAddress: token0Address, + walletAddress, + mintAmount, + hre, + }); + + // Mint and send Currency1 + console.log( + `Minting ${mintAmount} Currency1 to wallet ${walletAddress}...`, + ); + await mintCurrency({ + publicClient, + signer, + contractAddress: token1Address, + walletAddress, + mintAmount, + hre, + }); + + for (let receiver of wallets) { + const amount = mintAmount / 10; + await sendCurrency({ + publicClient, + signer, + currencyContract: token0Contract, + contractAddress: token0Address, + walletAddress: receiver, + mintAmount: amount, + hre, + }); + await sendCurrency({ + publicClient, + signer, + currencyContract: token1Contract, + contractAddress: token1Address, + walletAddress: receiver, + mintAmount: amount, + hre, + }); + } + + // 3. ROUTER: ADD LIQUIDITY + const routerArtifact = + await hre.artifacts.readArtifact("UniswapV2Router01"); + + // Mint liquidity + console.log("Adding liquidity..."); + + const hash = await wallet.sendMessage({ + to: routerAddress, + feeCredit: BigInt(10_000_000), + value: BigInt(0), + refundTo: wallet.address, + data: encodeFunctionData({ + abi: routerArtifact.abi, + functionName: "addLiquidity", + args: [pairAddress, walletAddress], + }), + tokens: [ + { + id: token0Id, + amount: BigInt(mintCurrency0Amount), + }, + { + id: token1Id, + amount: BigInt(mintCurrency1Amount), + }, + ], + }); + + await waitTillCompleted(publicClient, shardNumber(walletAddress), hash); + + // Log balances in the pair contract + const pairCurrency0Balance = + await token0Contract.getCurrencyBalanceOf(pairAddress); + console.log("Pair Balance of Currency0:", pairCurrency0Balance.toString()); + + const pairCurrency1Balance = + await token1Contract.getCurrencyBalanceOf(pairAddress); + console.log("Pair Balance of Currency1:", pairCurrency1Balance.toString()); + + console.log("Liquidity added..."); + + // Retrieve and log reserves from the pair + const [reserve0, reserve1] = await pair.getReserves(); + console.log( + `ADDLIQUIDITY RESULT: Reserves - Currency0: ${reserve0.toString()}, Currency1: ${reserve1.toString()}`, + ); + + // Check and log liquidity provider balance + const lpBalance = await pair.getCurrencyBalanceOf(walletAddress); + console.log( + "ADDLIQUIDITY RESULT: Liquidity provider balance in wallet:", + lpBalance.toString(), + ); + + // Retrieve and log total supply for the pair + const totalSupply = await pair.getCurrencyTotalSupply(); + console.log( + "ADDLIQUIDITY RESULT: Total supply of pair tokens:", + totalSupply.toString(), + ); + }, + ); diff --git a/tasks/util/currencyUtils.ts b/tasks/util/currencyUtils.ts index 4b9e977..6de3b11 100644 --- a/tasks/util/currencyUtils.ts +++ b/tasks/util/currencyUtils.ts @@ -1,4 +1,4 @@ -import { shardNumber } from "@nilfoundation/hardhat-plugin/dist/utils/conversion"; +import {shardNumber} from "@nilfoundation/hardhat-plugin/dist/utils/conversion"; import { ExternalMessageEnvelope, bytesToHex, @@ -6,20 +6,20 @@ import { toHex, waitTillCompleted, } from "@nilfoundation/niljs"; -import { encodeFunctionData } from "viem"; +import {encodeFunctionData} from "viem"; /** * Function to mint and send currency from a contract. */ export async function mintAndSendCurrency({ - publicClient, - signer, - currencyContract, - contractAddress, - walletAddress, - mintAmount, - hre, -}) { + publicClient, + signer, + currencyContract, + contractAddress, + walletAddress, + mintAmount, + hre, + }) { const artifact = await hre.artifacts.readArtifact("Currency"); const chainId = await publicClient.chainId(); @@ -53,22 +53,77 @@ export async function mintAndSendCurrency({ await sendAndAwait(publicClient, walletAddress, message); } +export async function mintCurrency( + { + publicClient, + signer, + contractAddress, + walletAddress, + mintAmount, + hre, + }) { + const artifact = await hre.artifacts.readArtifact("Currency"); + const chainId = await publicClient.chainId(); + + // Mint currency + let seqNo = await publicClient.getMessageCount(contractAddress, "latest"); + let message = createExternalMessage({ + contractAddress, + chainId, + seqNo, + functionName: "mintCurrency", + args: [mintAmount], + abi: artifact.abi, + }); + message.authData = await message.sign(signer); + await sendAndAwait(publicClient, walletAddress, message); +} + +export async function sendCurrency( + { + publicClient, + signer, + currencyContract, + contractAddress, + walletAddress, + amount, + hre, + } +) { + const artifact = await hre.artifacts.readArtifact("Currency"); + const chainId = await publicClient.chainId(); + await sleep(3000); + + // Send currency to wallet + const seqNo = await publicClient.getMessageCount(contractAddress, "latest"); + const message = createExternalMessage({ + contractAddress, + chainId, + seqNo, + functionName: "sendCurrency", + args: [walletAddress, await currencyContract.getCurrencyId(), amount], + abi: artifact.abi, + }); + message.authData = await message.sign(signer); + await sendAndAwait(publicClient, walletAddress, message); +} + /** * Helper function to create an ExternalMessageEnvelope. */ function createExternalMessage({ - contractAddress, - chainId, - seqNo, - functionName, - args, - abi, -}) { + contractAddress, + chainId, + seqNo, + functionName, + args, + abi, + }) { return new ExternalMessageEnvelope({ to: hexToBytes(contractAddress), chainId, seqno: seqNo, - data: hexToBytes(encodeFunctionData({ abi, functionName, args })), + data: hexToBytes(encodeFunctionData({abi, functionName, args})), authData: new Uint8Array(0), isDeploy: false, });