From 434db6d64e46c9af08dff3717faf02767dde9ffa Mon Sep 17 00:00:00 2001 From: Leon Prouger Date: Mon, 16 Aug 2021 14:38:56 +0300 Subject: [PATCH 1/8] Wallet actions refactor --- server/.npmrc | 1 + server/package.json | 4 + server/src/token.js | 0 server/src/utils/wallet/actions/create.js | 177 +++++++++------------ server/src/utils/wallet/actions/failure.js | 18 +++ server/src/utils/wallet/actions/index.js | 79 +-------- server/src/utils/wallet/actions/pending.js | 20 +++ server/src/utils/wallet/actions/success.js | 53 ++++++ server/src/utils/wallet/actions/utils.js | 29 +++- 9 files changed, 203 insertions(+), 178 deletions(-) create mode 100644 server/.npmrc create mode 100644 server/src/token.js create mode 100644 server/src/utils/wallet/actions/failure.js create mode 100644 server/src/utils/wallet/actions/pending.js create mode 100644 server/src/utils/wallet/actions/success.js diff --git a/server/.npmrc b/server/.npmrc new file mode 100644 index 000000000..b6f27f135 --- /dev/null +++ b/server/.npmrc @@ -0,0 +1 @@ +engine-strict=true diff --git a/server/package.json b/server/package.json index 3bf89fb64..d2c5544fc 100644 --- a/server/package.json +++ b/server/package.json @@ -15,6 +15,10 @@ "repl-debug": "node --inspect --experimental-repl-await scripts/repl.js", "lint-fix": "standard --fix" }, + "engines" : { + "node" : ">=15.13.0", + "npm": ">=6.0.0" + }, "repository": { "type": "git", "url": "git+https://github.com/fuseio/fuse-studio.git" diff --git a/server/src/token.js b/server/src/token.js new file mode 100644 index 000000000..e69de29bb diff --git a/server/src/utils/wallet/actions/create.js b/server/src/utils/wallet/actions/create.js index 450dc5a35..f8b36fa25 100644 --- a/server/src/utils/wallet/actions/create.js +++ b/server/src/utils/wallet/actions/create.js @@ -2,143 +2,116 @@ const { get } = require('lodash') const { getParamsFromMethodData } = require('@utils/abi') const mongoose = require('mongoose') const WalletAction = mongoose.model('WalletAction') -const { formatActionData } = require('./utils') +const { formatActionData, getActionsTypes } = require('./utils') -const handleCreateWalletJob = async (job) => { +const makeCreateWalletAction = ({ name } = {}) => async (job) => { + const data = formatActionData(job.data) return new WalletAction({ - name: 'createWallet', + name: name || job.name, job: mongoose.Types.ObjectId(job._id), - data: formatActionData(job.data), + data, communityAddress: job.communityAddress || job.data.communityAddress, - walletAddress: job.data.walletAddress + walletAddress: job.data.walletAddress, + tokenAddress: data.tokenAddress }).save() } -const handleRelayJob = async (job) => { - const { walletModule, methodName } = job.data - if (walletModule === 'CommunityManager' && methodName === 'joinCommunity') { - return new WalletAction({ - name: 'joinCommunity', - job: mongoose.Types.ObjectId(job._id), - data: formatActionData(job.data), - walletAddress: job.data.walletAddress, - communityAddress: job.communityAddress || job.data.communityAddress - }).save() - } else if (walletModule === 'TransferManager' && methodName === 'transferToken') { - const walletModuleABI = require(`@constants/abi/${walletModule}`) - const { methodData } = job.data - const { _to, _amount, _token, _wallet } = getParamsFromMethodData(walletModuleABI, 'transferToken', methodData) - const tokenAddress = _token.toLowerCase() - const actionData = formatActionData({ ...job.data, transactionBody: { ...job.data.transactionBody, value: _amount, tokenAddress } }) - const receiverAction = await new WalletAction({ - name: 'receiveTokens', - job: mongoose.Types.ObjectId(job._id), - data: actionData, - tokenAddress, - walletAddress: _to, - communityAddress: job.communityAddress || job.data.communityAddress - }).save() - const senderAction = await new WalletAction({ - name: 'sendTokens', - job: mongoose.Types.ObjectId(job._id), - data: actionData, - walletAddress: _wallet, - tokenAddress, - communityAddress: job.communityAddress || job.data.communityAddress - }).save() - return { receiverAction, senderAction } - } else if (walletModule === 'TransferManager' && methodName === 'approveTokenAndCallContract') { - const walletModuleABI = require(`@constants/abi/${walletModule}`) - const { methodData } = job.data - const { _wallet, _token, _contract, _amount } = getParamsFromMethodData(walletModuleABI, 'approveTokenAndCallContract', methodData) - const tokenAddressIn = _token.toLowerCase() - const tokenAddressOut = get(job, 'data.txMetadata.currencyOut') +const createWalletAction = makeCreateWalletAction() - const swapAction = await new WalletAction({ - name: 'swapTokens', - job: mongoose.Types.ObjectId(job._id), - data: { - ...formatActionData(job.data), - spender: _contract, - value: _amount, - tokenAddress: tokenAddressIn, - timestamp: (Math.round(new Date().getTime() / 1000)).toString() - }, - tokenAddress: [tokenAddressIn, tokenAddressOut], - walletAddress: _wallet, - communityAddress: job.communityAddress || job.data.communityAddress - }).save() - return { swapAction } - } -} - -const handleFundToken = (job) => { - const data = formatActionData(job.data) +const handleReceiveTokens = (job) => { + const { walletModule } = job.data + const walletModuleABI = require(`@constants/abi/${walletModule}`) + const { methodData } = job.data + const { _to, _amount, _token } = getParamsFromMethodData(walletModuleABI, 'transferToken', methodData) + const tokenAddress = _token.toLowerCase() + const actionData = formatActionData({ ...job.data, transactionBody: { ...job.data.transactionBody, value: _amount, tokenAddress } }) return new WalletAction({ - name: 'tokenBonus', + name: 'receiveTokens', job: mongoose.Types.ObjectId(job._id), - data: { - ...data, - from: job.accountAddress - }, - tokenAddress: data.tokenAddress, - walletAddress: job.data.receiverAddress, + data: actionData, + tokenAddress, + walletAddress: _to, communityAddress: job.communityAddress || job.data.communityAddress }).save() } -const handleSetWalletOwnerJob = (job) => { +const handleSendTokens = (job) => { + const { walletModule } = job.data + const walletModuleABI = require(`@constants/abi/${walletModule}`) + const { methodData } = job.data + const { _amount, _token, _wallet } = getParamsFromMethodData(walletModuleABI, 'transferToken', methodData) + const tokenAddress = _token.toLowerCase() + const actionData = formatActionData({ ...job.data, transactionBody: { ...job.data.transactionBody, value: _amount, tokenAddress } }) return new WalletAction({ - name: 'createWallet', + name: 'sendTokens', job: mongoose.Types.ObjectId(job._id), - data: formatActionData(job.data), - walletAddress: job.data.walletAddress, + data: actionData, + walletAddress: _wallet, + tokenAddress, communityAddress: job.communityAddress || job.data.communityAddress }).save() } -const makeMintDeposited = async (job) => { - const data = formatActionData(job.data) +const handleSwapTokens = (job) => { + const { walletModule } = job.data + const walletModuleABI = require(`@constants/abi/${walletModule}`) + const { methodData } = job.data + const { _wallet, _token, _contract, _amount } = getParamsFromMethodData(walletModuleABI, 'approveTokenAndCallContract', methodData) + const tokenAddressIn = _token.toLowerCase() + const tokenAddressOut = get(job, 'data.txMetadata.currencyOut') + return new WalletAction({ - name: 'fiat-deposit', + name: 'swapTokens', job: mongoose.Types.ObjectId(job._id), - data, - tokenAddress: data.tokenAddress, - walletAddress: job.data.walletAddress, - communityAddress: job.data.communityAddress - }).save() + data: { + ...formatActionData(job.data), + spender: _contract, + value: _amount, + tokenAddress: tokenAddressIn, + timestamp: (Math.round(new Date().getTime() / 1000)).toString() + }, + tokenAddress: [tokenAddressIn, tokenAddressOut], + walletAddress: _wallet, + communityAddress: job.communityAddress || job.data.communityAddress + }) } -const handleClaimApy = async (job) => { +const handleFundToken = (job) => { const data = formatActionData(job.data) return new WalletAction({ - name: 'claimApy', + name: 'tokenBonus', job: mongoose.Types.ObjectId(job._id), - data: data, - tokenAddress: job.data.tokenAddress, - walletAddress: job.data.walletAddress + data: { + ...data, + from: job.accountAddress + }, + tokenAddress: data.tokenAddress, + walletAddress: job.data.receiverAddress, + communityAddress: job.communityAddress || job.data.communityAddress }).save() } -const jobCreationHandlers = { - createWallet: handleCreateWalletJob, - createForeignWallet: handleCreateWalletJob, - relay: handleRelayJob, - claimApy: handleClaimApy, - fundToken: handleFundToken, - mintDeposited: makeMintDeposited, - setWalletOwner: handleSetWalletOwnerJob +const handleSetWalletOwnerJob = makeCreateWalletAction({ name: 'createWallet' }) + +const makeMintDeposited = makeCreateWalletAction({ name: 'fiat-deposit' }) + +const specialActionHandlers = { + receiveTokens: handleReceiveTokens, + sendTokens: handleSendTokens, + swapTokens: handleSwapTokens, + setWalletOwner: handleSetWalletOwnerJob, + tokenBonus: handleFundToken, + 'fiat-deposit': makeMintDeposited } const createActionFromJob = async (job) => { try { - const makeActionFunc = jobCreationHandlers[job.name] - if (!makeActionFunc) { - console.warn(`No action is defined for ${job.name}`) - return + const actionTypes = [getActionsTypes(job)].flat() + for (let action of actionTypes) { + console.log(`Received action type of ${action}`) + const makeActionFunc = specialActionHandlers[action] || createWalletAction + await makeActionFunc(job) } - const actions = await makeActionFunc(job) - return actions } catch (err) { console.error(err) } diff --git a/server/src/utils/wallet/actions/failure.js b/server/src/utils/wallet/actions/failure.js new file mode 100644 index 000000000..e947093f4 --- /dev/null +++ b/server/src/utils/wallet/actions/failure.js @@ -0,0 +1,18 @@ +const mongoose = require('mongoose') +const WalletAction = mongoose.model('WalletAction') +const { formatActionData } = require('./utils') + +const failAndUpdateByJob = async (job) => { + const walletActions = await WalletAction.find({ job }) + for (const action of walletActions) { + action.set('status', 'failed') + action.set('data', { ...action.data, ...formatActionData(job.data) }) + action.set('failedAt', new Date()) + action.set('failReason', job.failReason) + await action.save() + } +} + +module.exports = { + failAndUpdateByJob +} diff --git a/server/src/utils/wallet/actions/index.js b/server/src/utils/wallet/actions/index.js index b53325103..c6bdb4823 100644 --- a/server/src/utils/wallet/actions/index.js +++ b/server/src/utils/wallet/actions/index.js @@ -1,81 +1,10 @@ -const mongoose = require('mongoose') -const WalletAction = mongoose.model('WalletAction') -const { get } = require('lodash') + const { createActionFromJob } = require('./create') +const { pendingAndUpdateByJob } = require('./pending') +const { successAndUpdateByJob, handleSubscriptionWebHook } = require('./success') +const { failAndUpdateByJob } = require('./failure') const { formatActionData } = require('./utils') -const handleSuccessCreateWalletJob = (action, job) => { - const formattedData = formatActionData(job.data) - action.set('status', get(formattedData, 'status')) - if (!action.walletAddress) { - action.set('walletAddress', job.data.walletAddress) - } - action.set('data', { ...action.data, ...formattedData }) - return action.save() -} - -const handleSuccessDefaultJob = (action, job) => { - const formattedData = formatActionData(job.data) - action.set('status', get(formattedData, 'status', 'succeeded')) - action.set('data', { ...action.data, ...formattedData }) - return action.save() -} - -const jobSuccessHandlers = { - createWallet: handleSuccessCreateWalletJob, - createForeignWallet: handleSuccessDefaultJob, - fundToken: handleSuccessDefaultJob, - relay: handleSuccessDefaultJob, - claimApy: handleSuccessDefaultJob, - mintDeposited: handleSuccessDefaultJob -} - -const handlePendingAction = (action, txHash) => { - action.set('data.txHash', txHash) - return action.save() -} - -const successAndUpdateByJob = async (job) => { - const jobHandler = jobSuccessHandlers[job.name] - if (!jobHandler) { - console.warn(`No action is defined for ${job.name}`) - return - } - const walletActions = await WalletAction.find({ job }) - for (const action of walletActions) { - await jobHandler(action, job) - } -} - -const pendingAndUpdateByJob = async (job, hash) => { - const walletActions = await WalletAction.find({ job }) - console.log(`found ${walletActions.length} - hash ${hash}`) - for (const action of walletActions) { - await handlePendingAction(action, hash) - } -} - -const failAndUpdateByJob = async (job) => { - const walletActions = await WalletAction.find({ job }) - for (const action of walletActions) { - action.set('status', 'failed') - action.set('data', { ...action.data, ...formatActionData(job.data) }) - action.set('failedAt', new Date()) - action.set('failReason', job.failReason) - await action.save() - } -} - -const handleSubscriptionWebHook = async (data) => { - return new WalletAction({ - name: 'receiveTokens', - data, - status: 'succeeded', - tokenAddress: data.tokenAddress, - walletAddress: data.walletAddress - }).save() -} - module.exports = { createActionFromJob, successAndUpdateByJob, diff --git a/server/src/utils/wallet/actions/pending.js b/server/src/utils/wallet/actions/pending.js new file mode 100644 index 000000000..a3a469cfd --- /dev/null +++ b/server/src/utils/wallet/actions/pending.js @@ -0,0 +1,20 @@ +const mongoose = require('mongoose') +const WalletAction = mongoose.model('WalletAction') + +const handlePendingAction = (action, txHash) => { + action.set('data.txHash', txHash) + return action.save() +} + +const pendingAndUpdateByJob = async (job, hash) => { + const walletActions = await WalletAction.find({ job }) + + console.log(`found ${walletActions.length} - hash ${hash}`) + for (const action of walletActions) { + await handlePendingAction(action, hash) + } +} + +module.exports = { + pendingAndUpdateByJob +} diff --git a/server/src/utils/wallet/actions/success.js b/server/src/utils/wallet/actions/success.js new file mode 100644 index 000000000..6df2b69db --- /dev/null +++ b/server/src/utils/wallet/actions/success.js @@ -0,0 +1,53 @@ +const mongoose = require('mongoose') +const WalletAction = mongoose.model('WalletAction') +const { get } = require('lodash') +const { formatActionData } = require('./utils') + +const handleSuccessCreateWalletJob = (action, job) => { + const formattedData = formatActionData(job.data) + action.set('status', get(formattedData, 'status')) + if (!action.walletAddress) { + action.set('walletAddress', job.data.walletAddress) + } + action.set('data', { ...action.data, ...formattedData }) + return action.save() +} + +const handleJobSuccess = (action, job) => { + const formattedData = formatActionData(job.data) + action.set('status', get(formattedData, 'status', 'succeeded')) + action.set('data', { ...action.data, ...formattedData }) + return action.save() +} + +const handleSubscriptionWebHook = async (data) => { + return new WalletAction({ + name: 'receiveTokens', + data, + status: 'succeeded', + tokenAddress: data.tokenAddress, + walletAddress: data.walletAddress + }).save() +} + +const specialActionHandlers = { + createWallet: handleSuccessCreateWalletJob +} + +const successAndUpdateByJob = async (job) => { + try { + const walletActions = await WalletAction.find({ job }) + for (let action of walletActions) { + console.log(`Received action type of ${action.name} was successful`) + const sucessHandler = specialActionHandlers[action] || handleJobSuccess + await sucessHandler(action, job) + } + } catch (err) { + console.error(err) + } +} + +module.exports = { + successAndUpdateByJob, + handleSubscriptionWebHook +} diff --git a/server/src/utils/wallet/actions/utils.js b/server/src/utils/wallet/actions/utils.js index dae5bf3f9..f597dfd65 100644 --- a/server/src/utils/wallet/actions/utils.js +++ b/server/src/utils/wallet/actions/utils.js @@ -18,6 +18,33 @@ const formatActionData = ({ transactionBody, txHash, bonusType, externalId, deta txHash }, identity) +const getActionsTypes = (job) => { + if (job.name !== 'relay') { + return job.name + } + const { walletModule, methodName } = job.data + switch (walletModule) { + case 'CommunityManager': + switch (methodName) { + case 'joinCommunity': + return 'joinCommunity' + } + break + case 'TransferManager': + switch (methodName) { + case 'transferToken': + return ['sendTokens', 'receiveTokens'] + case 'approveTokenAndCallContract': + return 'swapTokens' + case 'callContract': + return 'sendTokens' + default: + return 'sendTokens' + } + } +} + module.exports = { - formatActionData + formatActionData, + getActionsTypes } From 73a82f4440201ee1d4c3c7c5d1543389d4cf5110 Mon Sep 17 00:00:00 2001 From: Leon Prouger Date: Wed, 18 Aug 2021 12:00:55 +0300 Subject: [PATCH 2/8] saving --- server/src/utils/token/Untitled-1 | 43 +++++++++++++++++++++++++++++++ 1 file changed, 43 insertions(+) create mode 100644 server/src/utils/token/Untitled-1 diff --git a/server/src/utils/token/Untitled-1 b/server/src/utils/token/Untitled-1 new file mode 100644 index 000000000..a40efc49e --- /dev/null +++ b/server/src/utils/token/Untitled-1 @@ -0,0 +1,43 @@ + +transferToken methodData + +struct: +- method sig +- wallet address +- token to transfer +- to +- amount + + +methodData: +from +https://explorer.fuse.io/tx/0x30c3d882c29e8ddb2a3b36cb45df97f00f46e01467ca64780c46479be60aeaed/internal-transactions + +0x2df546f4000000000000000000000000af21fb07aed5f2fcb2664b67f1f9a9de5faf4de0000000000000000000000000eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee00000000000000000000000057520b81f4045f883fd0a16353adfa1480c86c2800000000000000000000000000000000000000000000000fb88ef7839f48000000000000000000000000000000000000000000000000000000000000000000a00000000000000000000000000000000000000000000000000000000000000000 + + + + +example: +sig: transferToken(address,address,address,uint256,bytes) -> 0x2df546f +wallet: 0xAF21fb07AEd5F2fCB2664b67F1F9A9dE5FaF4DE0 -> af21fb07aed5f2fcb2664b67f1f9a9de5faf4de +token: native -> eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee + + + + +approveTokenAndCallContract + +0x09d22c8e000000000000000000000000e05e8cb522868ae468738d4a835b3de2ac1befc0000000000000000000000000249be57637d8b013ad64785404b24aebae9b098b000000000000000000000000fb76e9e7d88e308ab530330ed90e84a95257031900000000000000000000000000000000000000000000000821ab0d441498000000000000000000000000000000000000000000000000000000000000000000a0000000000000000000000000000000000000000000000000000000000000012438ed173900000000000000000000000000000000000000000000000821ab0d441498000000000000000000000000000000000000000000000000000000adba2c5806b13300000000000000000000000000000000000000000000000000000000000000a0000000000000000000000000e05e8cb522868ae468738d4a835b3de2ac1befc000000000000000000000000000000000000000000000000000000000611cb69b0000000000000000000000000000000000000000000000000000000000000003000000000000000000000000249be57637d8b013ad64785404b24aebae9b098b0000000000000000000000000be9e53fd7edac9f859882afdda116645287c629000000000000000000000000a722c13135930332eb3d749b2f0906559d2c5b9900000000000000000000000000000000000000000000000000000000 + +struct: +- method sig +- wallet address +- _contract +- amount +- callData: +821ab0d441498000000000000000000000000000000000000000000000000000000000000000000a0000000000000000000000000000000000000000000000000000000000000012438ed173900000000000000000000000000000000000000000000000821ab0d441498000000000000000000000000000000000000000000000000000000adba2c5806b13300000000000000000000000000000000000000000000000000000000000000a0000000000000000000000000e05e8cb522868ae468738d4a835b3de2ac1befc000000000000000000000000000000000000000000000000000000000611cb69b0000000000000000000000000000000000000000000000000000000000000003000000000000000000000000249be57637d8b013ad64785404b24aebae9b098b0000000000000000000000000be9e53fd7edac9f859882afdda116645287c629000000000000000000000000a722c13135930332eb3d749b2f0906559d2c5b9900000000000000000000000000000000000000000000000000000000 + + + +function invoke(address _target, uint _value, bytes calldata _data) \ No newline at end of file From 907a21bc34858c0e178b5e44df5ee4b83138bafd Mon Sep 17 00:00:00 2001 From: Leon Prouger Date: Thu, 19 Aug 2021 10:23:17 +0300 Subject: [PATCH 3/8] Working on method parsing --- server/package.json | 17 +- .../src/constants/abi/IUniswapV2Router02.json | 1924 +++++++++++++++++ server/src/constants/abi/SimpleList.json | 316 --- .../src/constants/abi/SimpleListFactory.json | 62 - server/src/utils/abi.js | 16 +- server/src/utils/wallet/actions/utils.js | 1 + server/test/index.js | 1 - server/test/utils/wallet/actions/test.spec.js | 18 + 8 files changed, 1971 insertions(+), 384 deletions(-) create mode 100644 server/src/constants/abi/IUniswapV2Router02.json delete mode 100644 server/src/constants/abi/SimpleList.json delete mode 100644 server/src/constants/abi/SimpleListFactory.json delete mode 100644 server/test/index.js create mode 100644 server/test/utils/wallet/actions/test.spec.js diff --git a/server/package.json b/server/package.json index d2c5544fc..61fe6c8a6 100644 --- a/server/package.json +++ b/server/package.json @@ -13,11 +13,18 @@ "lint": "standard", "repl": "node --experimental-repl-await scripts/repl.js", "repl-debug": "node --inspect --experimental-repl-await scripts/repl.js", - "lint-fix": "standard --fix" + "lint-fix": "standard --fix", + "test" : "mocha test" }, - "engines" : { - "node" : ">=15.13.0", - "npm": ">=6.0.0" + "engines": { + "node": ">=15.13.0", + "npm": ">=6.0.0" + }, + "mocha": { + "recursive": true, + "extension": [ + "js" + ] }, "repository": { "type": "git", @@ -103,6 +110,8 @@ "@babel/types": "^7.7.4", "@rigwild/apidoc-markdown": "^2.0.2", "apidoc": "^0.17.7", + "chai": "^4.3.4", + "mocha": "^9.0.3", "nodemon": "^1.18.11", "standard": "^12.0.1" } diff --git a/server/src/constants/abi/IUniswapV2Router02.json b/server/src/constants/abi/IUniswapV2Router02.json new file mode 100644 index 000000000..0fec08442 --- /dev/null +++ b/server/src/constants/abi/IUniswapV2Router02.json @@ -0,0 +1,1924 @@ +{ + "abi": [ + { + "inputs": [], + "name": "WETH", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "pure", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "tokenA", + "type": "address" + }, + { + "internalType": "address", + "name": "tokenB", + "type": "address" + }, + { + "internalType": "uint256", + "name": "amountADesired", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "amountBDesired", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "amountAMin", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "amountBMin", + "type": "uint256" + }, + { + "internalType": "address", + "name": "to", + "type": "address" + }, + { + "internalType": "uint256", + "name": "deadline", + "type": "uint256" + } + ], + "name": "addLiquidity", + "outputs": [ + { + "internalType": "uint256", + "name": "amountA", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "amountB", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "liquidity", + "type": "uint256" + } + ], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "token", + "type": "address" + }, + { + "internalType": "uint256", + "name": "amountTokenDesired", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "amountTokenMin", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "amountETHMin", + "type": "uint256" + }, + { + "internalType": "address", + "name": "to", + "type": "address" + }, + { + "internalType": "uint256", + "name": "deadline", + "type": "uint256" + } + ], + "name": "addLiquidityETH", + "outputs": [ + { + "internalType": "uint256", + "name": "amountToken", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "amountETH", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "liquidity", + "type": "uint256" + } + ], + "stateMutability": "payable", + "type": "function" + }, + { + "inputs": [], + "name": "factory", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "pure", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "amountOut", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "reserveIn", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "reserveOut", + "type": "uint256" + } + ], + "name": "getAmountIn", + "outputs": [ + { + "internalType": "uint256", + "name": "amountIn", + "type": "uint256" + } + ], + "stateMutability": "pure", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "amountIn", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "reserveIn", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "reserveOut", + "type": "uint256" + } + ], + "name": "getAmountOut", + "outputs": [ + { + "internalType": "uint256", + "name": "amountOut", + "type": "uint256" + } + ], + "stateMutability": "pure", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "amountOut", + "type": "uint256" + }, + { + "internalType": "address[]", + "name": "path", + "type": "address[]" + } + ], + "name": "getAmountsIn", + "outputs": [ + { + "internalType": "uint256[]", + "name": "amounts", + "type": "uint256[]" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "amountIn", + "type": "uint256" + }, + { + "internalType": "address[]", + "name": "path", + "type": "address[]" + } + ], + "name": "getAmountsOut", + "outputs": [ + { + "internalType": "uint256[]", + "name": "amounts", + "type": "uint256[]" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "amountA", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "reserveA", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "reserveB", + "type": "uint256" + } + ], + "name": "quote", + "outputs": [ + { + "internalType": "uint256", + "name": "amountB", + "type": "uint256" + } + ], + "stateMutability": "pure", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "tokenA", + "type": "address" + }, + { + "internalType": "address", + "name": "tokenB", + "type": "address" + }, + { + "internalType": "uint256", + "name": "liquidity", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "amountAMin", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "amountBMin", + "type": "uint256" + }, + { + "internalType": "address", + "name": "to", + "type": "address" + }, + { + "internalType": "uint256", + "name": "deadline", + "type": "uint256" + } + ], + "name": "removeLiquidity", + "outputs": [ + { + "internalType": "uint256", + "name": "amountA", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "amountB", + "type": "uint256" + } + ], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "token", + "type": "address" + }, + { + "internalType": "uint256", + "name": "liquidity", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "amountTokenMin", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "amountETHMin", + "type": "uint256" + }, + { + "internalType": "address", + "name": "to", + "type": "address" + }, + { + "internalType": "uint256", + "name": "deadline", + "type": "uint256" + } + ], + "name": "removeLiquidityETH", + "outputs": [ + { + "internalType": "uint256", + "name": "amountToken", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "amountETH", + "type": "uint256" + } + ], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "token", + "type": "address" + }, + { + "internalType": "uint256", + "name": "liquidity", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "amountTokenMin", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "amountETHMin", + "type": "uint256" + }, + { + "internalType": "address", + "name": "to", + "type": "address" + }, + { + "internalType": "uint256", + "name": "deadline", + "type": "uint256" + } + ], + "name": "removeLiquidityETHSupportingFeeOnTransferTokens", + "outputs": [ + { + "internalType": "uint256", + "name": "amountETH", + "type": "uint256" + } + ], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "token", + "type": "address" + }, + { + "internalType": "uint256", + "name": "liquidity", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "amountTokenMin", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "amountETHMin", + "type": "uint256" + }, + { + "internalType": "address", + "name": "to", + "type": "address" + }, + { + "internalType": "uint256", + "name": "deadline", + "type": "uint256" + }, + { + "internalType": "bool", + "name": "approveMax", + "type": "bool" + }, + { + "internalType": "uint8", + "name": "v", + "type": "uint8" + }, + { + "internalType": "bytes32", + "name": "r", + "type": "bytes32" + }, + { + "internalType": "bytes32", + "name": "s", + "type": "bytes32" + } + ], + "name": "removeLiquidityETHWithPermit", + "outputs": [ + { + "internalType": "uint256", + "name": "amountToken", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "amountETH", + "type": "uint256" + } + ], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "token", + "type": "address" + }, + { + "internalType": "uint256", + "name": "liquidity", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "amountTokenMin", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "amountETHMin", + "type": "uint256" + }, + { + "internalType": "address", + "name": "to", + "type": "address" + }, + { + "internalType": "uint256", + "name": "deadline", + "type": "uint256" + }, + { + "internalType": "bool", + "name": "approveMax", + "type": "bool" + }, + { + "internalType": "uint8", + "name": "v", + "type": "uint8" + }, + { + "internalType": "bytes32", + "name": "r", + "type": "bytes32" + }, + { + "internalType": "bytes32", + "name": "s", + "type": "bytes32" + } + ], + "name": "removeLiquidityETHWithPermitSupportingFeeOnTransferTokens", + "outputs": [ + { + "internalType": "uint256", + "name": "amountETH", + "type": "uint256" + } + ], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "tokenA", + "type": "address" + }, + { + "internalType": "address", + "name": "tokenB", + "type": "address" + }, + { + "internalType": "uint256", + "name": "liquidity", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "amountAMin", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "amountBMin", + "type": "uint256" + }, + { + "internalType": "address", + "name": "to", + "type": "address" + }, + { + "internalType": "uint256", + "name": "deadline", + "type": "uint256" + }, + { + "internalType": "bool", + "name": "approveMax", + "type": "bool" + }, + { + "internalType": "uint8", + "name": "v", + "type": "uint8" + }, + { + "internalType": "bytes32", + "name": "r", + "type": "bytes32" + }, + { + "internalType": "bytes32", + "name": "s", + "type": "bytes32" + } + ], + "name": "removeLiquidityWithPermit", + "outputs": [ + { + "internalType": "uint256", + "name": "amountA", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "amountB", + "type": "uint256" + } + ], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "amountOut", + "type": "uint256" + }, + { + "internalType": "address[]", + "name": "path", + "type": "address[]" + }, + { + "internalType": "address", + "name": "to", + "type": "address" + }, + { + "internalType": "uint256", + "name": "deadline", + "type": "uint256" + } + ], + "name": "swapETHForExactTokens", + "outputs": [ + { + "internalType": "uint256[]", + "name": "amounts", + "type": "uint256[]" + } + ], + "stateMutability": "payable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "amountOutMin", + "type": "uint256" + }, + { + "internalType": "address[]", + "name": "path", + "type": "address[]" + }, + { + "internalType": "address", + "name": "to", + "type": "address" + }, + { + "internalType": "uint256", + "name": "deadline", + "type": "uint256" + } + ], + "name": "swapExactETHForTokens", + "outputs": [ + { + "internalType": "uint256[]", + "name": "amounts", + "type": "uint256[]" + } + ], + "stateMutability": "payable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "amountOutMin", + "type": "uint256" + }, + { + "internalType": "address[]", + "name": "path", + "type": "address[]" + }, + { + "internalType": "address", + "name": "to", + "type": "address" + }, + { + "internalType": "uint256", + "name": "deadline", + "type": "uint256" + } + ], + "name": "swapExactETHForTokensSupportingFeeOnTransferTokens", + "outputs": [], + "stateMutability": "payable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "amountIn", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "amountOutMin", + "type": "uint256" + }, + { + "internalType": "address[]", + "name": "path", + "type": "address[]" + }, + { + "internalType": "address", + "name": "to", + "type": "address" + }, + { + "internalType": "uint256", + "name": "deadline", + "type": "uint256" + } + ], + "name": "swapExactTokensForETH", + "outputs": [ + { + "internalType": "uint256[]", + "name": "amounts", + "type": "uint256[]" + } + ], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "amountIn", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "amountOutMin", + "type": "uint256" + }, + { + "internalType": "address[]", + "name": "path", + "type": "address[]" + }, + { + "internalType": "address", + "name": "to", + "type": "address" + }, + { + "internalType": "uint256", + "name": "deadline", + "type": "uint256" + } + ], + "name": "swapExactTokensForETHSupportingFeeOnTransferTokens", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "amountIn", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "amountOutMin", + "type": "uint256" + }, + { + "internalType": "address[]", + "name": "path", + "type": "address[]" + }, + { + "internalType": "address", + "name": "to", + "type": "address" + }, + { + "internalType": "uint256", + "name": "deadline", + "type": "uint256" + } + ], + "name": "swapExactTokensForTokens", + "outputs": [ + { + "internalType": "uint256[]", + "name": "amounts", + "type": "uint256[]" + } + ], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "amountIn", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "amountOutMin", + "type": "uint256" + }, + { + "internalType": "address[]", + "name": "path", + "type": "address[]" + }, + { + "internalType": "address", + "name": "to", + "type": "address" + }, + { + "internalType": "uint256", + "name": "deadline", + "type": "uint256" + } + ], + "name": "swapExactTokensForTokensSupportingFeeOnTransferTokens", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "amountOut", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "amountInMax", + "type": "uint256" + }, + { + "internalType": "address[]", + "name": "path", + "type": "address[]" + }, + { + "internalType": "address", + "name": "to", + "type": "address" + }, + { + "internalType": "uint256", + "name": "deadline", + "type": "uint256" + } + ], + "name": "swapTokensForExactETH", + "outputs": [ + { + "internalType": "uint256[]", + "name": "amounts", + "type": "uint256[]" + } + ], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "amountOut", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "amountInMax", + "type": "uint256" + }, + { + "internalType": "address[]", + "name": "path", + "type": "address[]" + }, + { + "internalType": "address", + "name": "to", + "type": "address" + }, + { + "internalType": "uint256", + "name": "deadline", + "type": "uint256" + } + ], + "name": "swapTokensForExactTokens", + "outputs": [ + { + "internalType": "uint256[]", + "name": "amounts", + "type": "uint256[]" + } + ], + "stateMutability": "nonpayable", + "type": "function" + } + ], + "evm": { + "bytecode": { + "linkReferences": {}, + "object": "", + "opcodes": "", + "sourceMap": "" + }, + "deployedBytecode": { + "immutableReferences": {}, + "linkReferences": {}, + "object": "", + "opcodes": "", + "sourceMap": "" + } + }, + "interface": [ + { + "inputs": [], + "name": "WETH", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "pure", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "tokenA", + "type": "address" + }, + { + "internalType": "address", + "name": "tokenB", + "type": "address" + }, + { + "internalType": "uint256", + "name": "amountADesired", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "amountBDesired", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "amountAMin", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "amountBMin", + "type": "uint256" + }, + { + "internalType": "address", + "name": "to", + "type": "address" + }, + { + "internalType": "uint256", + "name": "deadline", + "type": "uint256" + } + ], + "name": "addLiquidity", + "outputs": [ + { + "internalType": "uint256", + "name": "amountA", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "amountB", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "liquidity", + "type": "uint256" + } + ], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "token", + "type": "address" + }, + { + "internalType": "uint256", + "name": "amountTokenDesired", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "amountTokenMin", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "amountETHMin", + "type": "uint256" + }, + { + "internalType": "address", + "name": "to", + "type": "address" + }, + { + "internalType": "uint256", + "name": "deadline", + "type": "uint256" + } + ], + "name": "addLiquidityETH", + "outputs": [ + { + "internalType": "uint256", + "name": "amountToken", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "amountETH", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "liquidity", + "type": "uint256" + } + ], + "stateMutability": "payable", + "type": "function" + }, + { + "inputs": [], + "name": "factory", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "pure", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "amountOut", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "reserveIn", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "reserveOut", + "type": "uint256" + } + ], + "name": "getAmountIn", + "outputs": [ + { + "internalType": "uint256", + "name": "amountIn", + "type": "uint256" + } + ], + "stateMutability": "pure", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "amountIn", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "reserveIn", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "reserveOut", + "type": "uint256" + } + ], + "name": "getAmountOut", + "outputs": [ + { + "internalType": "uint256", + "name": "amountOut", + "type": "uint256" + } + ], + "stateMutability": "pure", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "amountOut", + "type": "uint256" + }, + { + "internalType": "address[]", + "name": "path", + "type": "address[]" + } + ], + "name": "getAmountsIn", + "outputs": [ + { + "internalType": "uint256[]", + "name": "amounts", + "type": "uint256[]" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "amountIn", + "type": "uint256" + }, + { + "internalType": "address[]", + "name": "path", + "type": "address[]" + } + ], + "name": "getAmountsOut", + "outputs": [ + { + "internalType": "uint256[]", + "name": "amounts", + "type": "uint256[]" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "amountA", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "reserveA", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "reserveB", + "type": "uint256" + } + ], + "name": "quote", + "outputs": [ + { + "internalType": "uint256", + "name": "amountB", + "type": "uint256" + } + ], + "stateMutability": "pure", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "tokenA", + "type": "address" + }, + { + "internalType": "address", + "name": "tokenB", + "type": "address" + }, + { + "internalType": "uint256", + "name": "liquidity", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "amountAMin", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "amountBMin", + "type": "uint256" + }, + { + "internalType": "address", + "name": "to", + "type": "address" + }, + { + "internalType": "uint256", + "name": "deadline", + "type": "uint256" + } + ], + "name": "removeLiquidity", + "outputs": [ + { + "internalType": "uint256", + "name": "amountA", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "amountB", + "type": "uint256" + } + ], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "token", + "type": "address" + }, + { + "internalType": "uint256", + "name": "liquidity", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "amountTokenMin", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "amountETHMin", + "type": "uint256" + }, + { + "internalType": "address", + "name": "to", + "type": "address" + }, + { + "internalType": "uint256", + "name": "deadline", + "type": "uint256" + } + ], + "name": "removeLiquidityETH", + "outputs": [ + { + "internalType": "uint256", + "name": "amountToken", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "amountETH", + "type": "uint256" + } + ], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "token", + "type": "address" + }, + { + "internalType": "uint256", + "name": "liquidity", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "amountTokenMin", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "amountETHMin", + "type": "uint256" + }, + { + "internalType": "address", + "name": "to", + "type": "address" + }, + { + "internalType": "uint256", + "name": "deadline", + "type": "uint256" + } + ], + "name": "removeLiquidityETHSupportingFeeOnTransferTokens", + "outputs": [ + { + "internalType": "uint256", + "name": "amountETH", + "type": "uint256" + } + ], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "token", + "type": "address" + }, + { + "internalType": "uint256", + "name": "liquidity", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "amountTokenMin", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "amountETHMin", + "type": "uint256" + }, + { + "internalType": "address", + "name": "to", + "type": "address" + }, + { + "internalType": "uint256", + "name": "deadline", + "type": "uint256" + }, + { + "internalType": "bool", + "name": "approveMax", + "type": "bool" + }, + { + "internalType": "uint8", + "name": "v", + "type": "uint8" + }, + { + "internalType": "bytes32", + "name": "r", + "type": "bytes32" + }, + { + "internalType": "bytes32", + "name": "s", + "type": "bytes32" + } + ], + "name": "removeLiquidityETHWithPermit", + "outputs": [ + { + "internalType": "uint256", + "name": "amountToken", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "amountETH", + "type": "uint256" + } + ], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "token", + "type": "address" + }, + { + "internalType": "uint256", + "name": "liquidity", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "amountTokenMin", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "amountETHMin", + "type": "uint256" + }, + { + "internalType": "address", + "name": "to", + "type": "address" + }, + { + "internalType": "uint256", + "name": "deadline", + "type": "uint256" + }, + { + "internalType": "bool", + "name": "approveMax", + "type": "bool" + }, + { + "internalType": "uint8", + "name": "v", + "type": "uint8" + }, + { + "internalType": "bytes32", + "name": "r", + "type": "bytes32" + }, + { + "internalType": "bytes32", + "name": "s", + "type": "bytes32" + } + ], + "name": "removeLiquidityETHWithPermitSupportingFeeOnTransferTokens", + "outputs": [ + { + "internalType": "uint256", + "name": "amountETH", + "type": "uint256" + } + ], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "tokenA", + "type": "address" + }, + { + "internalType": "address", + "name": "tokenB", + "type": "address" + }, + { + "internalType": "uint256", + "name": "liquidity", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "amountAMin", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "amountBMin", + "type": "uint256" + }, + { + "internalType": "address", + "name": "to", + "type": "address" + }, + { + "internalType": "uint256", + "name": "deadline", + "type": "uint256" + }, + { + "internalType": "bool", + "name": "approveMax", + "type": "bool" + }, + { + "internalType": "uint8", + "name": "v", + "type": "uint8" + }, + { + "internalType": "bytes32", + "name": "r", + "type": "bytes32" + }, + { + "internalType": "bytes32", + "name": "s", + "type": "bytes32" + } + ], + "name": "removeLiquidityWithPermit", + "outputs": [ + { + "internalType": "uint256", + "name": "amountA", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "amountB", + "type": "uint256" + } + ], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "amountOut", + "type": "uint256" + }, + { + "internalType": "address[]", + "name": "path", + "type": "address[]" + }, + { + "internalType": "address", + "name": "to", + "type": "address" + }, + { + "internalType": "uint256", + "name": "deadline", + "type": "uint256" + } + ], + "name": "swapETHForExactTokens", + "outputs": [ + { + "internalType": "uint256[]", + "name": "amounts", + "type": "uint256[]" + } + ], + "stateMutability": "payable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "amountOutMin", + "type": "uint256" + }, + { + "internalType": "address[]", + "name": "path", + "type": "address[]" + }, + { + "internalType": "address", + "name": "to", + "type": "address" + }, + { + "internalType": "uint256", + "name": "deadline", + "type": "uint256" + } + ], + "name": "swapExactETHForTokens", + "outputs": [ + { + "internalType": "uint256[]", + "name": "amounts", + "type": "uint256[]" + } + ], + "stateMutability": "payable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "amountOutMin", + "type": "uint256" + }, + { + "internalType": "address[]", + "name": "path", + "type": "address[]" + }, + { + "internalType": "address", + "name": "to", + "type": "address" + }, + { + "internalType": "uint256", + "name": "deadline", + "type": "uint256" + } + ], + "name": "swapExactETHForTokensSupportingFeeOnTransferTokens", + "outputs": [], + "stateMutability": "payable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "amountIn", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "amountOutMin", + "type": "uint256" + }, + { + "internalType": "address[]", + "name": "path", + "type": "address[]" + }, + { + "internalType": "address", + "name": "to", + "type": "address" + }, + { + "internalType": "uint256", + "name": "deadline", + "type": "uint256" + } + ], + "name": "swapExactTokensForETH", + "outputs": [ + { + "internalType": "uint256[]", + "name": "amounts", + "type": "uint256[]" + } + ], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "amountIn", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "amountOutMin", + "type": "uint256" + }, + { + "internalType": "address[]", + "name": "path", + "type": "address[]" + }, + { + "internalType": "address", + "name": "to", + "type": "address" + }, + { + "internalType": "uint256", + "name": "deadline", + "type": "uint256" + } + ], + "name": "swapExactTokensForETHSupportingFeeOnTransferTokens", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "amountIn", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "amountOutMin", + "type": "uint256" + }, + { + "internalType": "address[]", + "name": "path", + "type": "address[]" + }, + { + "internalType": "address", + "name": "to", + "type": "address" + }, + { + "internalType": "uint256", + "name": "deadline", + "type": "uint256" + } + ], + "name": "swapExactTokensForTokens", + "outputs": [ + { + "internalType": "uint256[]", + "name": "amounts", + "type": "uint256[]" + } + ], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "amountIn", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "amountOutMin", + "type": "uint256" + }, + { + "internalType": "address[]", + "name": "path", + "type": "address[]" + }, + { + "internalType": "address", + "name": "to", + "type": "address" + }, + { + "internalType": "uint256", + "name": "deadline", + "type": "uint256" + } + ], + "name": "swapExactTokensForTokensSupportingFeeOnTransferTokens", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "amountOut", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "amountInMax", + "type": "uint256" + }, + { + "internalType": "address[]", + "name": "path", + "type": "address[]" + }, + { + "internalType": "address", + "name": "to", + "type": "address" + }, + { + "internalType": "uint256", + "name": "deadline", + "type": "uint256" + } + ], + "name": "swapTokensForExactETH", + "outputs": [ + { + "internalType": "uint256[]", + "name": "amounts", + "type": "uint256[]" + } + ], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "amountOut", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "amountInMax", + "type": "uint256" + }, + { + "internalType": "address[]", + "name": "path", + "type": "address[]" + }, + { + "internalType": "address", + "name": "to", + "type": "address" + }, + { + "internalType": "uint256", + "name": "deadline", + "type": "uint256" + } + ], + "name": "swapTokensForExactTokens", + "outputs": [ + { + "internalType": "uint256[]", + "name": "amounts", + "type": "uint256[]" + } + ], + "stateMutability": "nonpayable", + "type": "function" + } + ], + "bytecode": "" +} \ No newline at end of file diff --git a/server/src/constants/abi/SimpleList.json b/server/src/constants/abi/SimpleList.json deleted file mode 100644 index 2b1cd2811..000000000 --- a/server/src/constants/abi/SimpleList.json +++ /dev/null @@ -1,316 +0,0 @@ -[ - { - "constant": true, - "inputs": [ - { - "name": "", - "type": "address" - } - ], - "name": "admins", - "outputs": [ - { - "name": "", - "type": "uint256" - } - ], - "payable": false, - "stateMutability": "view", - "type": "function" - }, - { - "constant": false, - "inputs": [], - "name": "renounceOwnership", - "outputs": [], - "payable": false, - "stateMutability": "nonpayable", - "type": "function" - }, - { - "constant": true, - "inputs": [], - "name": "owner", - "outputs": [ - { - "name": "", - "type": "address" - } - ], - "payable": false, - "stateMutability": "view", - "type": "function" - }, - { - "constant": true, - "inputs": [], - "name": "isOwner", - "outputs": [ - { - "name": "", - "type": "bool" - } - ], - "payable": false, - "stateMutability": "view", - "type": "function" - }, - { - "constant": false, - "inputs": [ - { - "name": "newOwner", - "type": "address" - } - ], - "name": "transferOwnership", - "outputs": [], - "payable": false, - "stateMutability": "nonpayable", - "type": "function" - }, - { - "inputs": [ - { - "name": "admin", - "type": "address" - } - ], - "payable": false, - "stateMutability": "nonpayable", - "type": "constructor" - }, - { - "anonymous": false, - "inputs": [ - { - "indexed": false, - "name": "hash", - "type": "string" - } - ], - "name": "EntityAdded", - "type": "event" - }, - { - "anonymous": false, - "inputs": [ - { - "indexed": false, - "name": "hash", - "type": "string" - } - ], - "name": "EntityDeleted", - "type": "event" - }, - { - "anonymous": false, - "inputs": [ - { - "indexed": false, - "name": "oldHash", - "type": "string" - }, - { - "indexed": false, - "name": "newHash", - "type": "string" - } - ], - "name": "EntityReplaced", - "type": "event" - }, - { - "anonymous": false, - "inputs": [ - { - "indexed": false, - "name": "_address", - "type": "address" - } - ], - "name": "AdminAdded", - "type": "event" - }, - { - "anonymous": false, - "inputs": [ - { - "indexed": false, - "name": "_address", - "type": "address" - } - ], - "name": "AdminRemoved", - "type": "event" - }, - { - "anonymous": false, - "inputs": [ - { - "indexed": true, - "name": "previousOwner", - "type": "address" - }, - { - "indexed": true, - "name": "newOwner", - "type": "address" - } - ], - "name": "OwnershipTransferred", - "type": "event" - }, - { - "constant": true, - "inputs": [], - "name": "count", - "outputs": [ - { - "name": "", - "type": "uint256" - } - ], - "payable": false, - "stateMutability": "view", - "type": "function" - }, - { - "constant": false, - "inputs": [ - { - "name": "hash", - "type": "string" - } - ], - "name": "addEntity", - "outputs": [ - { - "name": "", - "type": "bool" - } - ], - "payable": false, - "stateMutability": "nonpayable", - "type": "function" - }, - { - "constant": true, - "inputs": [ - { - "name": "index", - "type": "uint256" - } - ], - "name": "getEntity", - "outputs": [ - { - "name": "", - "type": "string" - } - ], - "payable": false, - "stateMutability": "view", - "type": "function" - }, - { - "constant": false, - "inputs": [ - { - "name": "hash", - "type": "string" - } - ], - "name": "deleteEntity", - "outputs": [ - { - "name": "", - "type": "bool" - } - ], - "payable": false, - "stateMutability": "nonpayable", - "type": "function" - }, - { - "constant": false, - "inputs": [ - { - "name": "oldHash", - "type": "string" - }, - { - "name": "newHash", - "type": "string" - } - ], - "name": "replaceEntity", - "outputs": [ - { - "name": "", - "type": "bool" - } - ], - "payable": false, - "stateMutability": "nonpayable", - "type": "function" - }, - { - "constant": false, - "inputs": [ - { - "name": "_address", - "type": "address" - } - ], - "name": "addAdmin", - "outputs": [ - { - "name": "", - "type": "bool" - } - ], - "payable": false, - "stateMutability": "nonpayable", - "type": "function" - }, - { - "constant": false, - "inputs": [ - { - "name": "_address", - "type": "address" - } - ], - "name": "removeAdmin", - "outputs": [ - { - "name": "", - "type": "bool" - } - ], - "payable": false, - "stateMutability": "nonpayable", - "type": "function" - }, - { - "constant": true, - "inputs": [ - { - "name": "_address", - "type": "address" - } - ], - "name": "isAdmin", - "outputs": [ - { - "name": "", - "type": "bool" - } - ], - "payable": false, - "stateMutability": "view", - "type": "function" - } -] \ No newline at end of file diff --git a/server/src/constants/abi/SimpleListFactory.json b/server/src/constants/abi/SimpleListFactory.json deleted file mode 100644 index ca6284b1f..000000000 --- a/server/src/constants/abi/SimpleListFactory.json +++ /dev/null @@ -1,62 +0,0 @@ -[ - { - "constant": true, - "inputs": [ - { - "name": "", - "type": "address" - } - ], - "name": "tokenToListMap", - "outputs": [ - { - "name": "", - "type": "address" - } - ], - "payable": false, - "stateMutability": "view", - "type": "function" - }, - { - "anonymous": false, - "inputs": [ - { - "indexed": false, - "name": "list", - "type": "address" - }, - { - "indexed": false, - "name": "token", - "type": "address" - }, - { - "indexed": false, - "name": "admin", - "type": "address" - } - ], - "name": "SimpleListCreated", - "type": "event" - }, - { - "constant": false, - "inputs": [ - { - "name": "token", - "type": "address" - } - ], - "name": "createSimpleList", - "outputs": [ - { - "name": "", - "type": "address" - } - ], - "payable": false, - "stateMutability": "nonpayable", - "type": "function" - } -] \ No newline at end of file diff --git a/server/src/utils/abi.js b/server/src/utils/abi.js index 98d9979ab..2b62f057c 100644 --- a/server/src/utils/abi.js +++ b/server/src/utils/abi.js @@ -6,7 +6,21 @@ const getParamsFromMethodData = (abi, methodName, methodData) => { const params = Web3EthAbi.decodeParameters(methodABI.inputs, `0x${methodData.replace(methodSig, '')}`) return params } +class Signatures { + constructor () { + this.dict = {} + } + + addSigsnatures (abi, type) { + const abiFragments = abi.filter(obj => obj.type === type) + for (let abiFragment of abiFragments) { + const methodSig = Web3EthAbi.encodeFunctionSignature(abiFragment) + console.log({ methodSig }) + } + } +} module.exports = { - getParamsFromMethodData + getParamsFromMethodData, + Signatures } diff --git a/server/src/utils/wallet/actions/utils.js b/server/src/utils/wallet/actions/utils.js index f597dfd65..fb5db88d1 100644 --- a/server/src/utils/wallet/actions/utils.js +++ b/server/src/utils/wallet/actions/utils.js @@ -23,6 +23,7 @@ const getActionsTypes = (job) => { return job.name } const { walletModule, methodName } = job.data + console.log({ walletModule, methodName }) switch (walletModule) { case 'CommunityManager': switch (methodName) { diff --git a/server/test/index.js b/server/test/index.js deleted file mode 100644 index f102a6fd6..000000000 --- a/server/test/index.js +++ /dev/null @@ -1 +0,0 @@ -require('module-alias/register') diff --git a/server/test/utils/wallet/actions/test.spec.js b/server/test/utils/wallet/actions/test.spec.js new file mode 100644 index 000000000..ae11949d0 --- /dev/null +++ b/server/test/utils/wallet/actions/test.spec.js @@ -0,0 +1,18 @@ +require('module-alias/register') +const { expect } = require('chai') +const { getActionsTypes } = require('@utils/wallet/actions/utils') + +describe('getActionsTypes', () => { + it('swapTokens type', () => { + const job = { + name: 'relay', + data: { + walletModule: 'TransferManager', + methodName: 'approveTokenAndCallContract', + methodData: '0x2df546f4000000000000000000000000af21fb07aed5f2fcb2664b67f1f9a9de5faf4de0000000000000000000000000eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee00000000000000000000000057520b81f4045f883fd0a16353adfa1480c86c2800000000000000000000000000000000000000000000000fb88ef7839f48000000000000000000000000000000000000000000000000000000000000000000a00000000000000000000000000000000000000000000000000000000000000000' + } + } + console.log(getActionsTypes(job)) + expect(getActionsTypes(job)).to.equal('swapTokens') + }) +}) From 35fc892ebbf3ba448bfa66dd33013e7761c5baae Mon Sep 17 00:00:00 2001 From: Leon Prouger Date: Mon, 30 Aug 2021 11:27:56 +0300 Subject: [PATCH 4/8] Working on parsing --- server/src/services/abi/wallet.js | 11 ++++++++++ server/src/utils/abi.js | 35 +++++++++++++++++++++++++++---- server/src/utils/token/Untitled-1 | 1 + server/test/utils/abi.spec.js | 29 +++++++++++++++++++++++++ 4 files changed, 72 insertions(+), 4 deletions(-) create mode 100644 server/src/services/abi/wallet.js create mode 100644 server/test/utils/abi.spec.js diff --git a/server/src/services/abi/wallet.js b/server/src/services/abi/wallet.js new file mode 100644 index 000000000..fdb3b826c --- /dev/null +++ b/server/src/services/abi/wallet.js @@ -0,0 +1,11 @@ +const { Signatures } = require('@utils/abi') +const TransferManagerABI = require('@constants/abi/TransferManager') +const CommunityManagerABI = require('@constants/abi/CommunityManager') + +const signatures = new Signatures() +signatures.addContract('TransferManager', TransferManagerABI) +signatures.addContract('CommunityManager', CommunityManagerABI) + +module.exports = { + signatures +} diff --git a/server/src/utils/abi.js b/server/src/utils/abi.js index 2b62f057c..4bed8a052 100644 --- a/server/src/utils/abi.js +++ b/server/src/utils/abi.js @@ -6,21 +6,48 @@ const getParamsFromMethodData = (abi, methodName, methodData) => { const params = Web3EthAbi.decodeParameters(methodABI.inputs, `0x${methodData.replace(methodSig, '')}`) return params } + +const getParamsFromFragment = (abiFragment, methodName, methodData) => { + const methodSig = Web3EthAbi.encodeFunctionSignature(abiFragment) + const params = Web3EthAbi.decodeParameters(abiFragment.inputs, `0x${methodData.replace(methodSig, '')}`) + return params +} + +const extractSignature = (methodData) => { + methodData.split(10) +} class Signatures { constructor () { this.dict = {} } - addSigsnatures (abi, type) { - const abiFragments = abi.filter(obj => obj.type === type) + addContract (contractName, abi, type) { + const abiFragments = type ? abi.filter(obj => obj.type === type) : abi for (let abiFragment of abiFragments) { const methodSig = Web3EthAbi.encodeFunctionSignature(abiFragment) - console.log({ methodSig }) + this.dict[methodSig] = { ...abiFragment, contractName } } } + + getFragment (sig) { + return this.dict[sig] + } +} +class JobParser { + constructor (signatures) { + this.signatures = signatures + } + + parse ({ methodData }) { + const signature = extractSignature(methodData) + const methodABI = this.signatures.getFragment(signature) + const params = Web3EthAbi.decodeParameters(methodABI.inputs, `0x${methodData.replace(methodSig, '')}`) + } } module.exports = { getParamsFromMethodData, - Signatures + getParamsFromFragment, + Signatures, + JobParser } diff --git a/server/src/utils/token/Untitled-1 b/server/src/utils/token/Untitled-1 index a40efc49e..24a62433f 100644 --- a/server/src/utils/token/Untitled-1 +++ b/server/src/utils/token/Untitled-1 @@ -28,6 +28,7 @@ token: native -> eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee approveTokenAndCallContract +sig...............................|wallet address................................................|_contract.......................................................|amount 0x09d22c8e000000000000000000000000e05e8cb522868ae468738d4a835b3de2ac1befc0000000000000000000000000249be57637d8b013ad64785404b24aebae9b098b000000000000000000000000fb76e9e7d88e308ab530330ed90e84a95257031900000000000000000000000000000000000000000000000821ab0d441498000000000000000000000000000000000000000000000000000000000000000000a0000000000000000000000000000000000000000000000000000000000000012438ed173900000000000000000000000000000000000000000000000821ab0d441498000000000000000000000000000000000000000000000000000000adba2c5806b13300000000000000000000000000000000000000000000000000000000000000a0000000000000000000000000e05e8cb522868ae468738d4a835b3de2ac1befc000000000000000000000000000000000000000000000000000000000611cb69b0000000000000000000000000000000000000000000000000000000000000003000000000000000000000000249be57637d8b013ad64785404b24aebae9b098b0000000000000000000000000be9e53fd7edac9f859882afdda116645287c629000000000000000000000000a722c13135930332eb3d749b2f0906559d2c5b9900000000000000000000000000000000000000000000000000000000 struct: diff --git a/server/test/utils/abi.spec.js b/server/test/utils/abi.spec.js new file mode 100644 index 000000000..0ce91cae5 --- /dev/null +++ b/server/test/utils/abi.spec.js @@ -0,0 +1,29 @@ + +require('module-alias/register') +const { expect } = require('chai') +const { Signatures, JobParser } = require('@utils/abi') +const { signatures } = require('@services/abu/wallet') +const TransferManagerABI = require('@constants/abi/TransferManager') +const CommunityManagerABI = require('@constants/abi/CommunityManager') + +describe('Signatures', () => { + it('addContract should parse the contract ABI', () => { + const sigs = new Signatures() + sigs.addContract('TransferManager', TransferManagerABI) + expect(sigs.getFragment('0x2df546f4').name).to.equal('transferToken') + expect(sigs.getFragment('0x2df546f4').contractName).to.equal('TransferManager') + + expect(sigs.getFragment('0x09d22c8e').name).to.equal('approveTokenAndCallContract') + expect(sigs.getFragment('0x09d22c8e').contractName).to.equal('TransferManager') + + sigs.addContract('CommunityManager', CommunityManagerABI) + }) +}) + +describe.only('JobParser', () => { + it('Should parse arguements for tokenTransfer', () => { + const p = new JobParser(signatures) + const methodData = '0x2df546f4000000000000000000000000af21fb07aed5f2fcb2664b67f1f9a9de5faf4de0000000000000000000000000eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee00000000000000000000000057520b81f4045f883fd0a16353adfa1480c86c2800000000000000000000000000000000000000000000000fb88ef7839f48000000000000000000000000000000000000000000000000000000000000000000a00000000000000000000000000000000000000000000000000000000000000000' + console.log(p.parse({ methodData })) + }) +}) From f459210c5009b20e4fe3aabff54f65baa3d74434 Mon Sep 17 00:00:00 2001 From: Leon Prouger Date: Tue, 31 Aug 2021 15:21:41 +0300 Subject: [PATCH 5/8] Refactoring ABI utils --- .../src/constants/abi/IUniswapV2Router02.json | 2875 ++++++----------- server/src/services/abi/wallet.js | 12 +- server/src/utils/abi.js | 53 - server/src/utils/abi/index.js | 13 + server/src/utils/abi/methodParser.js | 19 + server/src/utils/abi/shared.js | 24 + server/src/utils/abi/signatureStore.js | 20 + server/test/utils/abi.spec.js | 53 +- 8 files changed, 1080 insertions(+), 1989 deletions(-) delete mode 100644 server/src/utils/abi.js create mode 100644 server/src/utils/abi/index.js create mode 100644 server/src/utils/abi/methodParser.js create mode 100644 server/src/utils/abi/shared.js create mode 100644 server/src/utils/abi/signatureStore.js diff --git a/server/src/constants/abi/IUniswapV2Router02.json b/server/src/constants/abi/IUniswapV2Router02.json index 0fec08442..04efb1fe8 100644 --- a/server/src/constants/abi/IUniswapV2Router02.json +++ b/server/src/constants/abi/IUniswapV2Router02.json @@ -1,1924 +1,953 @@ -{ - "abi": [ - { - "inputs": [], - "name": "WETH", - "outputs": [ - { - "internalType": "address", - "name": "", - "type": "address" - } - ], - "stateMutability": "pure", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "address", - "name": "tokenA", - "type": "address" - }, - { - "internalType": "address", - "name": "tokenB", - "type": "address" - }, - { - "internalType": "uint256", - "name": "amountADesired", - "type": "uint256" - }, - { - "internalType": "uint256", - "name": "amountBDesired", - "type": "uint256" - }, - { - "internalType": "uint256", - "name": "amountAMin", - "type": "uint256" - }, - { - "internalType": "uint256", - "name": "amountBMin", - "type": "uint256" - }, - { - "internalType": "address", - "name": "to", - "type": "address" - }, - { - "internalType": "uint256", - "name": "deadline", - "type": "uint256" - } - ], - "name": "addLiquidity", - "outputs": [ - { - "internalType": "uint256", - "name": "amountA", - "type": "uint256" - }, - { - "internalType": "uint256", - "name": "amountB", - "type": "uint256" - }, - { - "internalType": "uint256", - "name": "liquidity", - "type": "uint256" - } - ], - "stateMutability": "nonpayable", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "address", - "name": "token", - "type": "address" - }, - { - "internalType": "uint256", - "name": "amountTokenDesired", - "type": "uint256" - }, - { - "internalType": "uint256", - "name": "amountTokenMin", - "type": "uint256" - }, - { - "internalType": "uint256", - "name": "amountETHMin", - "type": "uint256" - }, - { - "internalType": "address", - "name": "to", - "type": "address" - }, - { - "internalType": "uint256", - "name": "deadline", - "type": "uint256" - } - ], - "name": "addLiquidityETH", - "outputs": [ - { - "internalType": "uint256", - "name": "amountToken", - "type": "uint256" - }, - { - "internalType": "uint256", - "name": "amountETH", - "type": "uint256" - }, - { - "internalType": "uint256", - "name": "liquidity", - "type": "uint256" - } - ], - "stateMutability": "payable", - "type": "function" - }, - { - "inputs": [], - "name": "factory", - "outputs": [ - { - "internalType": "address", - "name": "", - "type": "address" - } - ], - "stateMutability": "pure", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "uint256", - "name": "amountOut", - "type": "uint256" - }, - { - "internalType": "uint256", - "name": "reserveIn", - "type": "uint256" - }, - { - "internalType": "uint256", - "name": "reserveOut", - "type": "uint256" - } - ], - "name": "getAmountIn", - "outputs": [ - { - "internalType": "uint256", - "name": "amountIn", - "type": "uint256" - } - ], - "stateMutability": "pure", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "uint256", - "name": "amountIn", - "type": "uint256" - }, - { - "internalType": "uint256", - "name": "reserveIn", - "type": "uint256" - }, - { - "internalType": "uint256", - "name": "reserveOut", - "type": "uint256" - } - ], - "name": "getAmountOut", - "outputs": [ - { - "internalType": "uint256", - "name": "amountOut", - "type": "uint256" - } - ], - "stateMutability": "pure", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "uint256", - "name": "amountOut", - "type": "uint256" - }, - { - "internalType": "address[]", - "name": "path", - "type": "address[]" - } - ], - "name": "getAmountsIn", - "outputs": [ - { - "internalType": "uint256[]", - "name": "amounts", - "type": "uint256[]" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "uint256", - "name": "amountIn", - "type": "uint256" - }, - { - "internalType": "address[]", - "name": "path", - "type": "address[]" - } - ], - "name": "getAmountsOut", - "outputs": [ - { - "internalType": "uint256[]", - "name": "amounts", - "type": "uint256[]" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "uint256", - "name": "amountA", - "type": "uint256" - }, - { - "internalType": "uint256", - "name": "reserveA", - "type": "uint256" - }, - { - "internalType": "uint256", - "name": "reserveB", - "type": "uint256" - } - ], - "name": "quote", - "outputs": [ - { - "internalType": "uint256", - "name": "amountB", - "type": "uint256" - } - ], - "stateMutability": "pure", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "address", - "name": "tokenA", - "type": "address" - }, - { - "internalType": "address", - "name": "tokenB", - "type": "address" - }, - { - "internalType": "uint256", - "name": "liquidity", - "type": "uint256" - }, - { - "internalType": "uint256", - "name": "amountAMin", - "type": "uint256" - }, - { - "internalType": "uint256", - "name": "amountBMin", - "type": "uint256" - }, - { - "internalType": "address", - "name": "to", - "type": "address" - }, - { - "internalType": "uint256", - "name": "deadline", - "type": "uint256" - } - ], - "name": "removeLiquidity", - "outputs": [ - { - "internalType": "uint256", - "name": "amountA", - "type": "uint256" - }, - { - "internalType": "uint256", - "name": "amountB", - "type": "uint256" - } - ], - "stateMutability": "nonpayable", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "address", - "name": "token", - "type": "address" - }, - { - "internalType": "uint256", - "name": "liquidity", - "type": "uint256" - }, - { - "internalType": "uint256", - "name": "amountTokenMin", - "type": "uint256" - }, - { - "internalType": "uint256", - "name": "amountETHMin", - "type": "uint256" - }, - { - "internalType": "address", - "name": "to", - "type": "address" - }, - { - "internalType": "uint256", - "name": "deadline", - "type": "uint256" - } - ], - "name": "removeLiquidityETH", - "outputs": [ - { - "internalType": "uint256", - "name": "amountToken", - "type": "uint256" - }, - { - "internalType": "uint256", - "name": "amountETH", - "type": "uint256" - } - ], - "stateMutability": "nonpayable", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "address", - "name": "token", - "type": "address" - }, - { - "internalType": "uint256", - "name": "liquidity", - "type": "uint256" - }, - { - "internalType": "uint256", - "name": "amountTokenMin", - "type": "uint256" - }, - { - "internalType": "uint256", - "name": "amountETHMin", - "type": "uint256" - }, - { - "internalType": "address", - "name": "to", - "type": "address" - }, - { - "internalType": "uint256", - "name": "deadline", - "type": "uint256" - } - ], - "name": "removeLiquidityETHSupportingFeeOnTransferTokens", - "outputs": [ - { - "internalType": "uint256", - "name": "amountETH", - "type": "uint256" - } - ], - "stateMutability": "nonpayable", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "address", - "name": "token", - "type": "address" - }, - { - "internalType": "uint256", - "name": "liquidity", - "type": "uint256" - }, - { - "internalType": "uint256", - "name": "amountTokenMin", - "type": "uint256" - }, - { - "internalType": "uint256", - "name": "amountETHMin", - "type": "uint256" - }, - { - "internalType": "address", - "name": "to", - "type": "address" - }, - { - "internalType": "uint256", - "name": "deadline", - "type": "uint256" - }, - { - "internalType": "bool", - "name": "approveMax", - "type": "bool" - }, - { - "internalType": "uint8", - "name": "v", - "type": "uint8" - }, - { - "internalType": "bytes32", - "name": "r", - "type": "bytes32" - }, - { - "internalType": "bytes32", - "name": "s", - "type": "bytes32" - } - ], - "name": "removeLiquidityETHWithPermit", - "outputs": [ - { - "internalType": "uint256", - "name": "amountToken", - "type": "uint256" - }, - { - "internalType": "uint256", - "name": "amountETH", - "type": "uint256" - } - ], - "stateMutability": "nonpayable", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "address", - "name": "token", - "type": "address" - }, - { - "internalType": "uint256", - "name": "liquidity", - "type": "uint256" - }, - { - "internalType": "uint256", - "name": "amountTokenMin", - "type": "uint256" - }, - { - "internalType": "uint256", - "name": "amountETHMin", - "type": "uint256" - }, - { - "internalType": "address", - "name": "to", - "type": "address" - }, - { - "internalType": "uint256", - "name": "deadline", - "type": "uint256" - }, - { - "internalType": "bool", - "name": "approveMax", - "type": "bool" - }, - { - "internalType": "uint8", - "name": "v", - "type": "uint8" - }, - { - "internalType": "bytes32", - "name": "r", - "type": "bytes32" - }, - { - "internalType": "bytes32", - "name": "s", - "type": "bytes32" - } - ], - "name": "removeLiquidityETHWithPermitSupportingFeeOnTransferTokens", - "outputs": [ - { - "internalType": "uint256", - "name": "amountETH", - "type": "uint256" - } - ], - "stateMutability": "nonpayable", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "address", - "name": "tokenA", - "type": "address" - }, - { - "internalType": "address", - "name": "tokenB", - "type": "address" - }, - { - "internalType": "uint256", - "name": "liquidity", - "type": "uint256" - }, - { - "internalType": "uint256", - "name": "amountAMin", - "type": "uint256" - }, - { - "internalType": "uint256", - "name": "amountBMin", - "type": "uint256" - }, - { - "internalType": "address", - "name": "to", - "type": "address" - }, - { - "internalType": "uint256", - "name": "deadline", - "type": "uint256" - }, - { - "internalType": "bool", - "name": "approveMax", - "type": "bool" - }, - { - "internalType": "uint8", - "name": "v", - "type": "uint8" - }, - { - "internalType": "bytes32", - "name": "r", - "type": "bytes32" - }, - { - "internalType": "bytes32", - "name": "s", - "type": "bytes32" - } - ], - "name": "removeLiquidityWithPermit", - "outputs": [ - { - "internalType": "uint256", - "name": "amountA", - "type": "uint256" - }, - { - "internalType": "uint256", - "name": "amountB", - "type": "uint256" - } - ], - "stateMutability": "nonpayable", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "uint256", - "name": "amountOut", - "type": "uint256" - }, - { - "internalType": "address[]", - "name": "path", - "type": "address[]" - }, - { - "internalType": "address", - "name": "to", - "type": "address" - }, - { - "internalType": "uint256", - "name": "deadline", - "type": "uint256" - } - ], - "name": "swapETHForExactTokens", - "outputs": [ - { - "internalType": "uint256[]", - "name": "amounts", - "type": "uint256[]" - } - ], - "stateMutability": "payable", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "uint256", - "name": "amountOutMin", - "type": "uint256" - }, - { - "internalType": "address[]", - "name": "path", - "type": "address[]" - }, - { - "internalType": "address", - "name": "to", - "type": "address" - }, - { - "internalType": "uint256", - "name": "deadline", - "type": "uint256" - } - ], - "name": "swapExactETHForTokens", - "outputs": [ - { - "internalType": "uint256[]", - "name": "amounts", - "type": "uint256[]" - } - ], - "stateMutability": "payable", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "uint256", - "name": "amountOutMin", - "type": "uint256" - }, - { - "internalType": "address[]", - "name": "path", - "type": "address[]" - }, - { - "internalType": "address", - "name": "to", - "type": "address" - }, - { - "internalType": "uint256", - "name": "deadline", - "type": "uint256" - } - ], - "name": "swapExactETHForTokensSupportingFeeOnTransferTokens", - "outputs": [], - "stateMutability": "payable", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "uint256", - "name": "amountIn", - "type": "uint256" - }, - { - "internalType": "uint256", - "name": "amountOutMin", - "type": "uint256" - }, - { - "internalType": "address[]", - "name": "path", - "type": "address[]" - }, - { - "internalType": "address", - "name": "to", - "type": "address" - }, - { - "internalType": "uint256", - "name": "deadline", - "type": "uint256" - } - ], - "name": "swapExactTokensForETH", - "outputs": [ - { - "internalType": "uint256[]", - "name": "amounts", - "type": "uint256[]" - } - ], - "stateMutability": "nonpayable", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "uint256", - "name": "amountIn", - "type": "uint256" - }, - { - "internalType": "uint256", - "name": "amountOutMin", - "type": "uint256" - }, - { - "internalType": "address[]", - "name": "path", - "type": "address[]" - }, - { - "internalType": "address", - "name": "to", - "type": "address" - }, - { - "internalType": "uint256", - "name": "deadline", - "type": "uint256" - } - ], - "name": "swapExactTokensForETHSupportingFeeOnTransferTokens", - "outputs": [], - "stateMutability": "nonpayable", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "uint256", - "name": "amountIn", - "type": "uint256" - }, - { - "internalType": "uint256", - "name": "amountOutMin", - "type": "uint256" - }, - { - "internalType": "address[]", - "name": "path", - "type": "address[]" - }, - { - "internalType": "address", - "name": "to", - "type": "address" - }, - { - "internalType": "uint256", - "name": "deadline", - "type": "uint256" - } - ], - "name": "swapExactTokensForTokens", - "outputs": [ - { - "internalType": "uint256[]", - "name": "amounts", - "type": "uint256[]" - } - ], - "stateMutability": "nonpayable", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "uint256", - "name": "amountIn", - "type": "uint256" - }, - { - "internalType": "uint256", - "name": "amountOutMin", - "type": "uint256" - }, - { - "internalType": "address[]", - "name": "path", - "type": "address[]" - }, - { - "internalType": "address", - "name": "to", - "type": "address" - }, - { - "internalType": "uint256", - "name": "deadline", - "type": "uint256" - } - ], - "name": "swapExactTokensForTokensSupportingFeeOnTransferTokens", - "outputs": [], - "stateMutability": "nonpayable", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "uint256", - "name": "amountOut", - "type": "uint256" - }, - { - "internalType": "uint256", - "name": "amountInMax", - "type": "uint256" - }, - { - "internalType": "address[]", - "name": "path", - "type": "address[]" - }, - { - "internalType": "address", - "name": "to", - "type": "address" - }, - { - "internalType": "uint256", - "name": "deadline", - "type": "uint256" - } - ], - "name": "swapTokensForExactETH", - "outputs": [ - { - "internalType": "uint256[]", - "name": "amounts", - "type": "uint256[]" - } - ], - "stateMutability": "nonpayable", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "uint256", - "name": "amountOut", - "type": "uint256" - }, - { - "internalType": "uint256", - "name": "amountInMax", - "type": "uint256" - }, - { - "internalType": "address[]", - "name": "path", - "type": "address[]" - }, - { - "internalType": "address", - "name": "to", - "type": "address" - }, - { - "internalType": "uint256", - "name": "deadline", - "type": "uint256" - } - ], - "name": "swapTokensForExactTokens", - "outputs": [ - { - "internalType": "uint256[]", - "name": "amounts", - "type": "uint256[]" - } - ], - "stateMutability": "nonpayable", - "type": "function" - } - ], - "evm": { - "bytecode": { - "linkReferences": {}, - "object": "", - "opcodes": "", - "sourceMap": "" - }, - "deployedBytecode": { - "immutableReferences": {}, - "linkReferences": {}, - "object": "", - "opcodes": "", - "sourceMap": "" - } +[ + { + "inputs": [], + "name": "WETH", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "pure", + "type": "function" }, - "interface": [ - { - "inputs": [], - "name": "WETH", - "outputs": [ - { - "internalType": "address", - "name": "", - "type": "address" - } - ], - "stateMutability": "pure", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "address", - "name": "tokenA", - "type": "address" - }, - { - "internalType": "address", - "name": "tokenB", - "type": "address" - }, - { - "internalType": "uint256", - "name": "amountADesired", - "type": "uint256" - }, - { - "internalType": "uint256", - "name": "amountBDesired", - "type": "uint256" - }, - { - "internalType": "uint256", - "name": "amountAMin", - "type": "uint256" - }, - { - "internalType": "uint256", - "name": "amountBMin", - "type": "uint256" - }, - { - "internalType": "address", - "name": "to", - "type": "address" - }, - { - "internalType": "uint256", - "name": "deadline", - "type": "uint256" - } - ], - "name": "addLiquidity", - "outputs": [ - { - "internalType": "uint256", - "name": "amountA", - "type": "uint256" - }, - { - "internalType": "uint256", - "name": "amountB", - "type": "uint256" - }, - { - "internalType": "uint256", - "name": "liquidity", - "type": "uint256" - } - ], - "stateMutability": "nonpayable", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "address", - "name": "token", - "type": "address" - }, - { - "internalType": "uint256", - "name": "amountTokenDesired", - "type": "uint256" - }, - { - "internalType": "uint256", - "name": "amountTokenMin", - "type": "uint256" - }, - { - "internalType": "uint256", - "name": "amountETHMin", - "type": "uint256" - }, - { - "internalType": "address", - "name": "to", - "type": "address" - }, - { - "internalType": "uint256", - "name": "deadline", - "type": "uint256" - } - ], - "name": "addLiquidityETH", - "outputs": [ - { - "internalType": "uint256", - "name": "amountToken", - "type": "uint256" - }, - { - "internalType": "uint256", - "name": "amountETH", - "type": "uint256" - }, - { - "internalType": "uint256", - "name": "liquidity", - "type": "uint256" - } - ], - "stateMutability": "payable", - "type": "function" - }, - { - "inputs": [], - "name": "factory", - "outputs": [ - { - "internalType": "address", - "name": "", - "type": "address" - } - ], - "stateMutability": "pure", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "uint256", - "name": "amountOut", - "type": "uint256" - }, - { - "internalType": "uint256", - "name": "reserveIn", - "type": "uint256" - }, - { - "internalType": "uint256", - "name": "reserveOut", - "type": "uint256" - } - ], - "name": "getAmountIn", - "outputs": [ - { - "internalType": "uint256", - "name": "amountIn", - "type": "uint256" - } - ], - "stateMutability": "pure", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "uint256", - "name": "amountIn", - "type": "uint256" - }, - { - "internalType": "uint256", - "name": "reserveIn", - "type": "uint256" - }, - { - "internalType": "uint256", - "name": "reserveOut", - "type": "uint256" - } - ], - "name": "getAmountOut", - "outputs": [ - { - "internalType": "uint256", - "name": "amountOut", - "type": "uint256" - } - ], - "stateMutability": "pure", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "uint256", - "name": "amountOut", - "type": "uint256" - }, - { - "internalType": "address[]", - "name": "path", - "type": "address[]" - } - ], - "name": "getAmountsIn", - "outputs": [ - { - "internalType": "uint256[]", - "name": "amounts", - "type": "uint256[]" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "uint256", - "name": "amountIn", - "type": "uint256" - }, - { - "internalType": "address[]", - "name": "path", - "type": "address[]" - } - ], - "name": "getAmountsOut", - "outputs": [ - { - "internalType": "uint256[]", - "name": "amounts", - "type": "uint256[]" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "uint256", - "name": "amountA", - "type": "uint256" - }, - { - "internalType": "uint256", - "name": "reserveA", - "type": "uint256" - }, - { - "internalType": "uint256", - "name": "reserveB", - "type": "uint256" - } - ], - "name": "quote", - "outputs": [ - { - "internalType": "uint256", - "name": "amountB", - "type": "uint256" - } - ], - "stateMutability": "pure", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "address", - "name": "tokenA", - "type": "address" - }, - { - "internalType": "address", - "name": "tokenB", - "type": "address" - }, - { - "internalType": "uint256", - "name": "liquidity", - "type": "uint256" - }, - { - "internalType": "uint256", - "name": "amountAMin", - "type": "uint256" - }, - { - "internalType": "uint256", - "name": "amountBMin", - "type": "uint256" - }, - { - "internalType": "address", - "name": "to", - "type": "address" - }, - { - "internalType": "uint256", - "name": "deadline", - "type": "uint256" - } - ], - "name": "removeLiquidity", - "outputs": [ - { - "internalType": "uint256", - "name": "amountA", - "type": "uint256" - }, - { - "internalType": "uint256", - "name": "amountB", - "type": "uint256" - } - ], - "stateMutability": "nonpayable", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "address", - "name": "token", - "type": "address" - }, - { - "internalType": "uint256", - "name": "liquidity", - "type": "uint256" - }, - { - "internalType": "uint256", - "name": "amountTokenMin", - "type": "uint256" - }, - { - "internalType": "uint256", - "name": "amountETHMin", - "type": "uint256" - }, - { - "internalType": "address", - "name": "to", - "type": "address" - }, - { - "internalType": "uint256", - "name": "deadline", - "type": "uint256" - } - ], - "name": "removeLiquidityETH", - "outputs": [ - { - "internalType": "uint256", - "name": "amountToken", - "type": "uint256" - }, - { - "internalType": "uint256", - "name": "amountETH", - "type": "uint256" - } - ], - "stateMutability": "nonpayable", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "address", - "name": "token", - "type": "address" - }, - { - "internalType": "uint256", - "name": "liquidity", - "type": "uint256" - }, - { - "internalType": "uint256", - "name": "amountTokenMin", - "type": "uint256" - }, - { - "internalType": "uint256", - "name": "amountETHMin", - "type": "uint256" - }, - { - "internalType": "address", - "name": "to", - "type": "address" - }, - { - "internalType": "uint256", - "name": "deadline", - "type": "uint256" - } - ], - "name": "removeLiquidityETHSupportingFeeOnTransferTokens", - "outputs": [ - { - "internalType": "uint256", - "name": "amountETH", - "type": "uint256" - } - ], - "stateMutability": "nonpayable", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "address", - "name": "token", - "type": "address" - }, - { - "internalType": "uint256", - "name": "liquidity", - "type": "uint256" - }, - { - "internalType": "uint256", - "name": "amountTokenMin", - "type": "uint256" - }, - { - "internalType": "uint256", - "name": "amountETHMin", - "type": "uint256" - }, - { - "internalType": "address", - "name": "to", - "type": "address" - }, - { - "internalType": "uint256", - "name": "deadline", - "type": "uint256" - }, - { - "internalType": "bool", - "name": "approveMax", - "type": "bool" - }, - { - "internalType": "uint8", - "name": "v", - "type": "uint8" - }, - { - "internalType": "bytes32", - "name": "r", - "type": "bytes32" - }, - { - "internalType": "bytes32", - "name": "s", - "type": "bytes32" - } - ], - "name": "removeLiquidityETHWithPermit", - "outputs": [ - { - "internalType": "uint256", - "name": "amountToken", - "type": "uint256" - }, - { - "internalType": "uint256", - "name": "amountETH", - "type": "uint256" - } - ], - "stateMutability": "nonpayable", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "address", - "name": "token", - "type": "address" - }, - { - "internalType": "uint256", - "name": "liquidity", - "type": "uint256" - }, - { - "internalType": "uint256", - "name": "amountTokenMin", - "type": "uint256" - }, - { - "internalType": "uint256", - "name": "amountETHMin", - "type": "uint256" - }, - { - "internalType": "address", - "name": "to", - "type": "address" - }, - { - "internalType": "uint256", - "name": "deadline", - "type": "uint256" - }, - { - "internalType": "bool", - "name": "approveMax", - "type": "bool" - }, - { - "internalType": "uint8", - "name": "v", - "type": "uint8" - }, - { - "internalType": "bytes32", - "name": "r", - "type": "bytes32" - }, - { - "internalType": "bytes32", - "name": "s", - "type": "bytes32" - } - ], - "name": "removeLiquidityETHWithPermitSupportingFeeOnTransferTokens", - "outputs": [ - { - "internalType": "uint256", - "name": "amountETH", - "type": "uint256" - } - ], - "stateMutability": "nonpayable", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "address", - "name": "tokenA", - "type": "address" - }, - { - "internalType": "address", - "name": "tokenB", - "type": "address" - }, - { - "internalType": "uint256", - "name": "liquidity", - "type": "uint256" - }, - { - "internalType": "uint256", - "name": "amountAMin", - "type": "uint256" - }, - { - "internalType": "uint256", - "name": "amountBMin", - "type": "uint256" - }, - { - "internalType": "address", - "name": "to", - "type": "address" - }, - { - "internalType": "uint256", - "name": "deadline", - "type": "uint256" - }, - { - "internalType": "bool", - "name": "approveMax", - "type": "bool" - }, - { - "internalType": "uint8", - "name": "v", - "type": "uint8" - }, - { - "internalType": "bytes32", - "name": "r", - "type": "bytes32" - }, - { - "internalType": "bytes32", - "name": "s", - "type": "bytes32" - } - ], - "name": "removeLiquidityWithPermit", - "outputs": [ - { - "internalType": "uint256", - "name": "amountA", - "type": "uint256" - }, - { - "internalType": "uint256", - "name": "amountB", - "type": "uint256" - } - ], - "stateMutability": "nonpayable", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "uint256", - "name": "amountOut", - "type": "uint256" - }, - { - "internalType": "address[]", - "name": "path", - "type": "address[]" - }, - { - "internalType": "address", - "name": "to", - "type": "address" - }, - { - "internalType": "uint256", - "name": "deadline", - "type": "uint256" - } - ], - "name": "swapETHForExactTokens", - "outputs": [ - { - "internalType": "uint256[]", - "name": "amounts", - "type": "uint256[]" - } - ], - "stateMutability": "payable", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "uint256", - "name": "amountOutMin", - "type": "uint256" - }, - { - "internalType": "address[]", - "name": "path", - "type": "address[]" - }, - { - "internalType": "address", - "name": "to", - "type": "address" - }, - { - "internalType": "uint256", - "name": "deadline", - "type": "uint256" - } - ], - "name": "swapExactETHForTokens", - "outputs": [ - { - "internalType": "uint256[]", - "name": "amounts", - "type": "uint256[]" - } - ], - "stateMutability": "payable", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "uint256", - "name": "amountOutMin", - "type": "uint256" - }, - { - "internalType": "address[]", - "name": "path", - "type": "address[]" - }, - { - "internalType": "address", - "name": "to", - "type": "address" - }, - { - "internalType": "uint256", - "name": "deadline", - "type": "uint256" - } - ], - "name": "swapExactETHForTokensSupportingFeeOnTransferTokens", - "outputs": [], - "stateMutability": "payable", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "uint256", - "name": "amountIn", - "type": "uint256" - }, - { - "internalType": "uint256", - "name": "amountOutMin", - "type": "uint256" - }, - { - "internalType": "address[]", - "name": "path", - "type": "address[]" - }, - { - "internalType": "address", - "name": "to", - "type": "address" - }, - { - "internalType": "uint256", - "name": "deadline", - "type": "uint256" - } - ], - "name": "swapExactTokensForETH", - "outputs": [ - { - "internalType": "uint256[]", - "name": "amounts", - "type": "uint256[]" - } - ], - "stateMutability": "nonpayable", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "uint256", - "name": "amountIn", - "type": "uint256" - }, - { - "internalType": "uint256", - "name": "amountOutMin", - "type": "uint256" - }, - { - "internalType": "address[]", - "name": "path", - "type": "address[]" - }, - { - "internalType": "address", - "name": "to", - "type": "address" - }, - { - "internalType": "uint256", - "name": "deadline", - "type": "uint256" - } - ], - "name": "swapExactTokensForETHSupportingFeeOnTransferTokens", - "outputs": [], - "stateMutability": "nonpayable", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "uint256", - "name": "amountIn", - "type": "uint256" - }, - { - "internalType": "uint256", - "name": "amountOutMin", - "type": "uint256" - }, - { - "internalType": "address[]", - "name": "path", - "type": "address[]" - }, - { - "internalType": "address", - "name": "to", - "type": "address" - }, - { - "internalType": "uint256", - "name": "deadline", - "type": "uint256" - } - ], - "name": "swapExactTokensForTokens", - "outputs": [ - { - "internalType": "uint256[]", - "name": "amounts", - "type": "uint256[]" - } - ], - "stateMutability": "nonpayable", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "uint256", - "name": "amountIn", - "type": "uint256" - }, - { - "internalType": "uint256", - "name": "amountOutMin", - "type": "uint256" - }, - { - "internalType": "address[]", - "name": "path", - "type": "address[]" - }, - { - "internalType": "address", - "name": "to", - "type": "address" - }, - { - "internalType": "uint256", - "name": "deadline", - "type": "uint256" - } - ], - "name": "swapExactTokensForTokensSupportingFeeOnTransferTokens", - "outputs": [], - "stateMutability": "nonpayable", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "uint256", - "name": "amountOut", - "type": "uint256" - }, - { - "internalType": "uint256", - "name": "amountInMax", - "type": "uint256" - }, - { - "internalType": "address[]", - "name": "path", - "type": "address[]" - }, - { - "internalType": "address", - "name": "to", - "type": "address" - }, - { - "internalType": "uint256", - "name": "deadline", - "type": "uint256" - } - ], - "name": "swapTokensForExactETH", - "outputs": [ - { - "internalType": "uint256[]", - "name": "amounts", - "type": "uint256[]" - } - ], - "stateMutability": "nonpayable", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "uint256", - "name": "amountOut", - "type": "uint256" - }, - { - "internalType": "uint256", - "name": "amountInMax", - "type": "uint256" - }, - { - "internalType": "address[]", - "name": "path", - "type": "address[]" - }, - { - "internalType": "address", - "name": "to", - "type": "address" - }, - { - "internalType": "uint256", - "name": "deadline", - "type": "uint256" - } - ], - "name": "swapTokensForExactTokens", - "outputs": [ - { - "internalType": "uint256[]", - "name": "amounts", - "type": "uint256[]" - } - ], - "stateMutability": "nonpayable", - "type": "function" - } - ], - "bytecode": "" -} \ No newline at end of file + { + "inputs": [ + { + "internalType": "address", + "name": "tokenA", + "type": "address" + }, + { + "internalType": "address", + "name": "tokenB", + "type": "address" + }, + { + "internalType": "uint256", + "name": "amountADesired", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "amountBDesired", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "amountAMin", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "amountBMin", + "type": "uint256" + }, + { + "internalType": "address", + "name": "to", + "type": "address" + }, + { + "internalType": "uint256", + "name": "deadline", + "type": "uint256" + } + ], + "name": "addLiquidity", + "outputs": [ + { + "internalType": "uint256", + "name": "amountA", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "amountB", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "liquidity", + "type": "uint256" + } + ], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "token", + "type": "address" + }, + { + "internalType": "uint256", + "name": "amountTokenDesired", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "amountTokenMin", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "amountETHMin", + "type": "uint256" + }, + { + "internalType": "address", + "name": "to", + "type": "address" + }, + { + "internalType": "uint256", + "name": "deadline", + "type": "uint256" + } + ], + "name": "addLiquidityETH", + "outputs": [ + { + "internalType": "uint256", + "name": "amountToken", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "amountETH", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "liquidity", + "type": "uint256" + } + ], + "stateMutability": "payable", + "type": "function" + }, + { + "inputs": [], + "name": "factory", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "pure", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "amountOut", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "reserveIn", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "reserveOut", + "type": "uint256" + } + ], + "name": "getAmountIn", + "outputs": [ + { + "internalType": "uint256", + "name": "amountIn", + "type": "uint256" + } + ], + "stateMutability": "pure", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "amountIn", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "reserveIn", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "reserveOut", + "type": "uint256" + } + ], + "name": "getAmountOut", + "outputs": [ + { + "internalType": "uint256", + "name": "amountOut", + "type": "uint256" + } + ], + "stateMutability": "pure", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "amountOut", + "type": "uint256" + }, + { + "internalType": "address[]", + "name": "path", + "type": "address[]" + } + ], + "name": "getAmountsIn", + "outputs": [ + { + "internalType": "uint256[]", + "name": "amounts", + "type": "uint256[]" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "amountIn", + "type": "uint256" + }, + { + "internalType": "address[]", + "name": "path", + "type": "address[]" + } + ], + "name": "getAmountsOut", + "outputs": [ + { + "internalType": "uint256[]", + "name": "amounts", + "type": "uint256[]" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "amountA", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "reserveA", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "reserveB", + "type": "uint256" + } + ], + "name": "quote", + "outputs": [ + { + "internalType": "uint256", + "name": "amountB", + "type": "uint256" + } + ], + "stateMutability": "pure", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "tokenA", + "type": "address" + }, + { + "internalType": "address", + "name": "tokenB", + "type": "address" + }, + { + "internalType": "uint256", + "name": "liquidity", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "amountAMin", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "amountBMin", + "type": "uint256" + }, + { + "internalType": "address", + "name": "to", + "type": "address" + }, + { + "internalType": "uint256", + "name": "deadline", + "type": "uint256" + } + ], + "name": "removeLiquidity", + "outputs": [ + { + "internalType": "uint256", + "name": "amountA", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "amountB", + "type": "uint256" + } + ], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "token", + "type": "address" + }, + { + "internalType": "uint256", + "name": "liquidity", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "amountTokenMin", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "amountETHMin", + "type": "uint256" + }, + { + "internalType": "address", + "name": "to", + "type": "address" + }, + { + "internalType": "uint256", + "name": "deadline", + "type": "uint256" + } + ], + "name": "removeLiquidityETH", + "outputs": [ + { + "internalType": "uint256", + "name": "amountToken", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "amountETH", + "type": "uint256" + } + ], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "token", + "type": "address" + }, + { + "internalType": "uint256", + "name": "liquidity", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "amountTokenMin", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "amountETHMin", + "type": "uint256" + }, + { + "internalType": "address", + "name": "to", + "type": "address" + }, + { + "internalType": "uint256", + "name": "deadline", + "type": "uint256" + } + ], + "name": "removeLiquidityETHSupportingFeeOnTransferTokens", + "outputs": [ + { + "internalType": "uint256", + "name": "amountETH", + "type": "uint256" + } + ], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "token", + "type": "address" + }, + { + "internalType": "uint256", + "name": "liquidity", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "amountTokenMin", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "amountETHMin", + "type": "uint256" + }, + { + "internalType": "address", + "name": "to", + "type": "address" + }, + { + "internalType": "uint256", + "name": "deadline", + "type": "uint256" + }, + { + "internalType": "bool", + "name": "approveMax", + "type": "bool" + }, + { + "internalType": "uint8", + "name": "v", + "type": "uint8" + }, + { + "internalType": "bytes32", + "name": "r", + "type": "bytes32" + }, + { + "internalType": "bytes32", + "name": "s", + "type": "bytes32" + } + ], + "name": "removeLiquidityETHWithPermit", + "outputs": [ + { + "internalType": "uint256", + "name": "amountToken", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "amountETH", + "type": "uint256" + } + ], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "token", + "type": "address" + }, + { + "internalType": "uint256", + "name": "liquidity", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "amountTokenMin", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "amountETHMin", + "type": "uint256" + }, + { + "internalType": "address", + "name": "to", + "type": "address" + }, + { + "internalType": "uint256", + "name": "deadline", + "type": "uint256" + }, + { + "internalType": "bool", + "name": "approveMax", + "type": "bool" + }, + { + "internalType": "uint8", + "name": "v", + "type": "uint8" + }, + { + "internalType": "bytes32", + "name": "r", + "type": "bytes32" + }, + { + "internalType": "bytes32", + "name": "s", + "type": "bytes32" + } + ], + "name": "removeLiquidityETHWithPermitSupportingFeeOnTransferTokens", + "outputs": [ + { + "internalType": "uint256", + "name": "amountETH", + "type": "uint256" + } + ], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "tokenA", + "type": "address" + }, + { + "internalType": "address", + "name": "tokenB", + "type": "address" + }, + { + "internalType": "uint256", + "name": "liquidity", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "amountAMin", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "amountBMin", + "type": "uint256" + }, + { + "internalType": "address", + "name": "to", + "type": "address" + }, + { + "internalType": "uint256", + "name": "deadline", + "type": "uint256" + }, + { + "internalType": "bool", + "name": "approveMax", + "type": "bool" + }, + { + "internalType": "uint8", + "name": "v", + "type": "uint8" + }, + { + "internalType": "bytes32", + "name": "r", + "type": "bytes32" + }, + { + "internalType": "bytes32", + "name": "s", + "type": "bytes32" + } + ], + "name": "removeLiquidityWithPermit", + "outputs": [ + { + "internalType": "uint256", + "name": "amountA", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "amountB", + "type": "uint256" + } + ], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "amountOut", + "type": "uint256" + }, + { + "internalType": "address[]", + "name": "path", + "type": "address[]" + }, + { + "internalType": "address", + "name": "to", + "type": "address" + }, + { + "internalType": "uint256", + "name": "deadline", + "type": "uint256" + } + ], + "name": "swapETHForExactTokens", + "outputs": [ + { + "internalType": "uint256[]", + "name": "amounts", + "type": "uint256[]" + } + ], + "stateMutability": "payable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "amountOutMin", + "type": "uint256" + }, + { + "internalType": "address[]", + "name": "path", + "type": "address[]" + }, + { + "internalType": "address", + "name": "to", + "type": "address" + }, + { + "internalType": "uint256", + "name": "deadline", + "type": "uint256" + } + ], + "name": "swapExactETHForTokens", + "outputs": [ + { + "internalType": "uint256[]", + "name": "amounts", + "type": "uint256[]" + } + ], + "stateMutability": "payable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "amountOutMin", + "type": "uint256" + }, + { + "internalType": "address[]", + "name": "path", + "type": "address[]" + }, + { + "internalType": "address", + "name": "to", + "type": "address" + }, + { + "internalType": "uint256", + "name": "deadline", + "type": "uint256" + } + ], + "name": "swapExactETHForTokensSupportingFeeOnTransferTokens", + "outputs": [], + "stateMutability": "payable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "amountIn", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "amountOutMin", + "type": "uint256" + }, + { + "internalType": "address[]", + "name": "path", + "type": "address[]" + }, + { + "internalType": "address", + "name": "to", + "type": "address" + }, + { + "internalType": "uint256", + "name": "deadline", + "type": "uint256" + } + ], + "name": "swapExactTokensForETH", + "outputs": [ + { + "internalType": "uint256[]", + "name": "amounts", + "type": "uint256[]" + } + ], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "amountIn", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "amountOutMin", + "type": "uint256" + }, + { + "internalType": "address[]", + "name": "path", + "type": "address[]" + }, + { + "internalType": "address", + "name": "to", + "type": "address" + }, + { + "internalType": "uint256", + "name": "deadline", + "type": "uint256" + } + ], + "name": "swapExactTokensForETHSupportingFeeOnTransferTokens", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "amountIn", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "amountOutMin", + "type": "uint256" + }, + { + "internalType": "address[]", + "name": "path", + "type": "address[]" + }, + { + "internalType": "address", + "name": "to", + "type": "address" + }, + { + "internalType": "uint256", + "name": "deadline", + "type": "uint256" + } + ], + "name": "swapExactTokensForTokens", + "outputs": [ + { + "internalType": "uint256[]", + "name": "amounts", + "type": "uint256[]" + } + ], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "amountIn", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "amountOutMin", + "type": "uint256" + }, + { + "internalType": "address[]", + "name": "path", + "type": "address[]" + }, + { + "internalType": "address", + "name": "to", + "type": "address" + }, + { + "internalType": "uint256", + "name": "deadline", + "type": "uint256" + } + ], + "name": "swapExactTokensForTokensSupportingFeeOnTransferTokens", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "amountOut", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "amountInMax", + "type": "uint256" + }, + { + "internalType": "address[]", + "name": "path", + "type": "address[]" + }, + { + "internalType": "address", + "name": "to", + "type": "address" + }, + { + "internalType": "uint256", + "name": "deadline", + "type": "uint256" + } + ], + "name": "swapTokensForExactETH", + "outputs": [ + { + "internalType": "uint256[]", + "name": "amounts", + "type": "uint256[]" + } + ], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "amountOut", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "amountInMax", + "type": "uint256" + }, + { + "internalType": "address[]", + "name": "path", + "type": "address[]" + }, + { + "internalType": "address", + "name": "to", + "type": "address" + }, + { + "internalType": "uint256", + "name": "deadline", + "type": "uint256" + } + ], + "name": "swapTokensForExactTokens", + "outputs": [ + { + "internalType": "uint256[]", + "name": "amounts", + "type": "uint256[]" + } + ], + "stateMutability": "nonpayable", + "type": "function" + } +] \ No newline at end of file diff --git a/server/src/services/abi/wallet.js b/server/src/services/abi/wallet.js index fdb3b826c..13ef70969 100644 --- a/server/src/services/abi/wallet.js +++ b/server/src/services/abi/wallet.js @@ -1,11 +1,13 @@ -const { Signatures } = require('@utils/abi') +const { SignatureStore } = require('@utils/abi') const TransferManagerABI = require('@constants/abi/TransferManager') const CommunityManagerABI = require('@constants/abi/CommunityManager') +const IUniswapV2Router02ABI = require('@constants/abi/IUniswapV2Router02') -const signatures = new Signatures() -signatures.addContract('TransferManager', TransferManagerABI) -signatures.addContract('CommunityManager', CommunityManagerABI) +const signatureStore = new SignatureStore() +signatureStore.addContract('TransferManager', TransferManagerABI) +signatureStore.addContract('CommunityManager', CommunityManagerABI) +signatureStore.addContract('FuseswapRouter', IUniswapV2Router02ABI) module.exports = { - signatures + signatureStore } diff --git a/server/src/utils/abi.js b/server/src/utils/abi.js deleted file mode 100644 index 4bed8a052..000000000 --- a/server/src/utils/abi.js +++ /dev/null @@ -1,53 +0,0 @@ -const Web3EthAbi = require('web3-eth-abi') - -const getParamsFromMethodData = (abi, methodName, methodData) => { - const methodABI = abi.filter(obj => obj.name === methodName)[0] - const methodSig = Web3EthAbi.encodeFunctionSignature(methodABI) - const params = Web3EthAbi.decodeParameters(methodABI.inputs, `0x${methodData.replace(methodSig, '')}`) - return params -} - -const getParamsFromFragment = (abiFragment, methodName, methodData) => { - const methodSig = Web3EthAbi.encodeFunctionSignature(abiFragment) - const params = Web3EthAbi.decodeParameters(abiFragment.inputs, `0x${methodData.replace(methodSig, '')}`) - return params -} - -const extractSignature = (methodData) => { - methodData.split(10) -} -class Signatures { - constructor () { - this.dict = {} - } - - addContract (contractName, abi, type) { - const abiFragments = type ? abi.filter(obj => obj.type === type) : abi - for (let abiFragment of abiFragments) { - const methodSig = Web3EthAbi.encodeFunctionSignature(abiFragment) - this.dict[methodSig] = { ...abiFragment, contractName } - } - } - - getFragment (sig) { - return this.dict[sig] - } -} -class JobParser { - constructor (signatures) { - this.signatures = signatures - } - - parse ({ methodData }) { - const signature = extractSignature(methodData) - const methodABI = this.signatures.getFragment(signature) - const params = Web3EthAbi.decodeParameters(methodABI.inputs, `0x${methodData.replace(methodSig, '')}`) - } -} - -module.exports = { - getParamsFromMethodData, - getParamsFromFragment, - Signatures, - JobParser -} diff --git a/server/src/utils/abi/index.js b/server/src/utils/abi/index.js new file mode 100644 index 000000000..ec0858d37 --- /dev/null +++ b/server/src/utils/abi/index.js @@ -0,0 +1,13 @@ +const { + getParamsFromMethodData, + getParamsFromFragment +} = require('./shared') +const MethodParser = require('./methodParser') +const SignatureStore = require('./signatureStore') + +module.exports = { + getParamsFromMethodData, + getParamsFromFragment, + SignatureStore, + MethodParser +} diff --git a/server/src/utils/abi/methodParser.js b/server/src/utils/abi/methodParser.js new file mode 100644 index 000000000..c691de462 --- /dev/null +++ b/server/src/utils/abi/methodParser.js @@ -0,0 +1,19 @@ +const Web3EthAbi = require('web3-eth-abi') +const { + extractSignature +} = require('./shared') + +class MethodParser { + constructor (signatureStore) { + this.signatureStore = signatureStore + } + + parse (methodData) { + const signature = extractSignature(methodData) + const sigData = this.signatureStore.getFragment(signature) + const params = Web3EthAbi.decodeParameters(sigData.inputs, `0x${methodData.replace(signature, '')}`) + return { params, contractName: sigData.contractName, methodName: sigData.name } + } +} + +module.exports = MethodParser diff --git a/server/src/utils/abi/shared.js b/server/src/utils/abi/shared.js new file mode 100644 index 000000000..38b37dca1 --- /dev/null +++ b/server/src/utils/abi/shared.js @@ -0,0 +1,24 @@ +const Web3EthAbi = require('web3-eth-abi') + +const getParamsFromMethodData = (abi, methodName, methodData) => { + const methodABI = abi.filter(obj => obj.name === methodName)[0] + const methodSig = Web3EthAbi.encodeFunctionSignature(methodABI) + const params = Web3EthAbi.decodeParameters(methodABI.inputs, `0x${methodData.replace(methodSig, '')}`) + return params +} + +const getParamsFromFragment = (abiFragment, methodName, methodData) => { + const methodSig = Web3EthAbi.encodeFunctionSignature(abiFragment) + const params = Web3EthAbi.decodeParameters(abiFragment.inputs, `0x${methodData.replace(methodSig, '')}`) + return params +} + +const extractSignature = (methodData) => { + return methodData.slice(0, 10) +} + +module.exports = { + getParamsFromMethodData, + getParamsFromFragment, + extractSignature +} diff --git a/server/src/utils/abi/signatureStore.js b/server/src/utils/abi/signatureStore.js new file mode 100644 index 000000000..3452d0820 --- /dev/null +++ b/server/src/utils/abi/signatureStore.js @@ -0,0 +1,20 @@ +const Web3EthAbi = require('web3-eth-abi') + +class SignatureStore { + constructor () { + this.dict = {} + } + + addContract (contractName, abi, type) { + const abiFragments = type ? abi.filter(obj => obj.type === type) : abi + for (let abiFragment of abiFragments) { + const methodSig = Web3EthAbi.encodeFunctionSignature(abiFragment) + this.dict[methodSig] = { ...abiFragment, contractName } + } + } + + getFragment (sig) { + return this.dict[sig] + } +} +module.exports = SignatureStore diff --git a/server/test/utils/abi.spec.js b/server/test/utils/abi.spec.js index 0ce91cae5..03032ad3e 100644 --- a/server/test/utils/abi.spec.js +++ b/server/test/utils/abi.spec.js @@ -1,14 +1,14 @@ require('module-alias/register') const { expect } = require('chai') -const { Signatures, JobParser } = require('@utils/abi') -const { signatures } = require('@services/abu/wallet') +const { SignatureStore, MethodParser } = require('@utils/abi') +const { signatureStore } = require('@services/abi/wallet') const TransferManagerABI = require('@constants/abi/TransferManager') const CommunityManagerABI = require('@constants/abi/CommunityManager') -describe('Signatures', () => { +describe('SignatureStore', () => { it('addContract should parse the contract ABI', () => { - const sigs = new Signatures() + const sigs = new SignatureStore() sigs.addContract('TransferManager', TransferManagerABI) expect(sigs.getFragment('0x2df546f4').name).to.equal('transferToken') expect(sigs.getFragment('0x2df546f4').contractName).to.equal('TransferManager') @@ -20,10 +20,47 @@ describe('Signatures', () => { }) }) -describe.only('JobParser', () => { - it('Should parse arguements for tokenTransfer', () => { - const p = new JobParser(signatures) +describe('MethodParser', () => { + it('Should parse arguements for transferToken', () => { + const p = new MethodParser(signatureStore) const methodData = '0x2df546f4000000000000000000000000af21fb07aed5f2fcb2664b67f1f9a9de5faf4de0000000000000000000000000eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee00000000000000000000000057520b81f4045f883fd0a16353adfa1480c86c2800000000000000000000000000000000000000000000000fb88ef7839f48000000000000000000000000000000000000000000000000000000000000000000a00000000000000000000000000000000000000000000000000000000000000000' - console.log(p.parse({ methodData })) + const { params, contractName, methodName } = p.parse(methodData) + expect(contractName).to.equal('TransferManager') + expect(methodName).to.equal('transferToken') + expect(params._wallet).to.equal('0xAF21fb07AEd5F2fCB2664b67F1F9A9dE5FaF4DE0') + expect(params._token).to.equal('0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE') + expect(params._amount).to.equal('290000000000000000000') + }) + + it('Should parse arguements for approveTokenAndCallContract', () => { + const p = new MethodParser(signatureStore) + const methodData = '0x09d22c8e000000000000000000000000e05e8cb522868ae468738d4a835b3de2ac1befc0000000000000000000000000249be57637d8b013ad64785404b24aebae9b098b000000000000000000000000fb76e9e7d88e308ab530330ed90e84a95257031900000000000000000000000000000000000000000000000821ab0d441498000000000000000000000000000000000000000000000000000000000000000000a0000000000000000000000000000000000000000000000000000000000000012438ed173900000000000000000000000000000000000000000000000821ab0d441498000000000000000000000000000000000000000000000000000000adba2c5806b13300000000000000000000000000000000000000000000000000000000000000a0000000000000000000000000e05e8cb522868ae468738d4a835b3de2ac1befc000000000000000000000000000000000000000000000000000000000611cb69b0000000000000000000000000000000000000000000000000000000000000003000000000000000000000000249be57637d8b013ad64785404b24aebae9b098b0000000000000000000000000be9e53fd7edac9f859882afdda116645287c629000000000000000000000000a722c13135930332eb3d749b2f0906559d2c5b9900000000000000000000000000000000000000000000000000000000' + const data = '0x38ed173900000000000000000000000000000000000000000000000821ab0d441498000000000000000000000000000000000000000000000000000000adba2c5806b13300000000000000000000000000000000000000000000000000000000000000a0000000000000000000000000e05e8cb522868ae468738d4a835b3de2ac1befc000000000000000000000000000000000000000000000000000000000611cb69b0000000000000000000000000000000000000000000000000000000000000003000000000000000000000000249be57637d8b013ad64785404b24aebae9b098b0000000000000000000000000be9e53fd7edac9f859882afdda116645287c629000000000000000000000000a722c13135930332eb3d749b2f0906559d2c5b99' + const { params, contractName, methodName } = p.parse(methodData) + // console.log(parsed) + expect(contractName).to.equal('TransferManager') + expect(methodName).to.equal('approveTokenAndCallContract') + expect(params._wallet).to.equal('0xE05E8cB522868ae468738D4a835B3dE2ac1Befc0') + expect(params._token).to.equal('0x249BE57637D8B013Ad64785404b24aeBaE9B098B') + expect(params._contract).to.equal('0xFB76e9E7d88E308aB530330eD90e84a952570319') + expect(params._amount).to.equal('150000000000000000000') + expect(params._data).to.equal(data) + + function checkInner (methodData) { + const { params, contractName, methodName } = p.parse(methodData) + console.log({ params }) + expect(contractName).to.equal('FuseswapRouter') + expect(methodName).to.equal('swapExactTokensForTokens') + expect(params.amountIn).to.equal('150000000000000000000') + expect(params.amountOutMin).to.equal('48899870589104435') + expect(params.path).to.deep.equal([ + '0x249BE57637D8B013Ad64785404b24aeBaE9B098B', + '0x0BE9e53fd7EDaC9F859882AfdDa116645287C629', + '0xa722c13135930332Eb3d749B2F0906559D2C5b99' + ]) + expect(params.to).to.equal('0xE05E8cB522868ae468738D4a835B3dE2ac1Befc0') + expect(params.deadline).to.equal('1629271707') + } + checkInner(params._data) }) }) From b69403bfbb4c63c04cfdfa4f22a5f3b161f030b5 Mon Sep 17 00:00:00 2001 From: Leon Prouger Date: Thu, 2 Sep 2021 11:51:30 +0300 Subject: [PATCH 6/8] Finishing actions refactor and add more actions --- .../src/constants/abi/MultiRewardProgram.json | 493 ++++++++++++++++++ .../constants/abi/SingleRewardProgram.json | 319 ++++++++++++ server/src/routes/api/v2/relay.js | 4 +- server/src/services/wallet/relayJobParser.js | 6 + .../wallet.js => wallet/signatureStore.js} | 8 +- server/src/tasks/sqs/relay.js | 67 +-- server/src/tasks/sqs/upgrade.js | 2 +- server/src/utils/{jobs.js => jobs/index.js} | 0 .../utils/{relay.js => jobs/relay/index.js} | 4 +- server/src/utils/jobs/relay/parser.js | 28 + server/src/utils/wallet/actions/create.js | 40 +- server/src/utils/wallet/actions/utils.js | 58 ++- server/test/utils/abi.spec.js | 2 +- server/test/utils/wallet/actions/test.spec.js | 9 +- 14 files changed, 942 insertions(+), 98 deletions(-) create mode 100644 server/src/constants/abi/MultiRewardProgram.json create mode 100644 server/src/constants/abi/SingleRewardProgram.json create mode 100644 server/src/services/wallet/relayJobParser.js rename server/src/services/{abi/wallet.js => wallet/signatureStore.js} (60%) rename server/src/utils/{jobs.js => jobs/index.js} (100%) rename server/src/utils/{relay.js => jobs/relay/index.js} (97%) create mode 100644 server/src/utils/jobs/relay/parser.js diff --git a/server/src/constants/abi/MultiRewardProgram.json b/server/src/constants/abi/MultiRewardProgram.json new file mode 100644 index 000000000..0f8f29f31 --- /dev/null +++ b/server/src/constants/abi/MultiRewardProgram.json @@ -0,0 +1,493 @@ +[ + { + "type": "constructor", + "stateMutability": "nonpayable", + "payable": false, + "inputs": [ + { "type": "address", "name": "_owner", "internalType": "address" }, + { "type": "address", "name": "_stakingToken", "internalType": "address" } + ] + }, + { + "type": "event", + "name": "OwnerChanged", + "inputs": [ + { + "type": "address", + "name": "oldOwner", + "internalType": "address", + "indexed": false + }, + { + "type": "address", + "name": "newOwner", + "internalType": "address", + "indexed": false + } + ], + "anonymous": false + }, + { + "type": "event", + "name": "OwnerNominated", + "inputs": [ + { + "type": "address", + "name": "newOwner", + "internalType": "address", + "indexed": false + } + ], + "anonymous": false + }, + { + "type": "event", + "name": "PauseChanged", + "inputs": [ + { + "type": "bool", + "name": "isPaused", + "internalType": "bool", + "indexed": false + } + ], + "anonymous": false + }, + { + "type": "event", + "name": "Recovered", + "inputs": [ + { + "type": "address", + "name": "token", + "internalType": "address", + "indexed": false + }, + { + "type": "uint256", + "name": "amount", + "internalType": "uint256", + "indexed": false + } + ], + "anonymous": false + }, + { + "type": "event", + "name": "RewardAdded", + "inputs": [ + { + "type": "uint256", + "name": "reward", + "internalType": "uint256", + "indexed": false + } + ], + "anonymous": false + }, + { + "type": "event", + "name": "RewardPaid", + "inputs": [ + { + "type": "address", + "name": "user", + "internalType": "address", + "indexed": true + }, + { + "type": "address", + "name": "rewardsToken", + "internalType": "address", + "indexed": true + }, + { + "type": "uint256", + "name": "reward", + "internalType": "uint256", + "indexed": false + } + ], + "anonymous": false + }, + { + "type": "event", + "name": "RewardsDurationUpdated", + "inputs": [ + { + "type": "address", + "name": "token", + "internalType": "address", + "indexed": false + }, + { + "type": "uint256", + "name": "newDuration", + "internalType": "uint256", + "indexed": false + } + ], + "anonymous": false + }, + { + "type": "event", + "name": "Staked", + "inputs": [ + { + "type": "address", + "name": "user", + "internalType": "address", + "indexed": true + }, + { + "type": "uint256", + "name": "amount", + "internalType": "uint256", + "indexed": false + } + ], + "anonymous": false + }, + { + "type": "event", + "name": "Withdrawn", + "inputs": [ + { + "type": "address", + "name": "user", + "internalType": "address", + "indexed": true + }, + { + "type": "uint256", + "name": "amount", + "internalType": "uint256", + "indexed": false + } + ], + "anonymous": false + }, + { + "type": "function", + "stateMutability": "nonpayable", + "payable": false, + "outputs": [], + "name": "acceptOwnership", + "inputs": [], + "constant": false + }, + { + "type": "function", + "stateMutability": "nonpayable", + "payable": false, + "outputs": [], + "name": "addReward", + "inputs": [ + { "type": "address", "name": "_rewardsToken", "internalType": "address" }, + { + "type": "address", + "name": "_rewardsDistributor", + "internalType": "address" + }, + { + "type": "uint256", + "name": "_rewardsDuration", + "internalType": "uint256" + } + ], + "constant": false + }, + { + "type": "function", + "stateMutability": "view", + "payable": false, + "outputs": [{ "type": "uint256", "name": "", "internalType": "uint256" }], + "name": "balanceOf", + "inputs": [ + { "type": "address", "name": "account", "internalType": "address" } + ], + "constant": true + }, + { + "type": "function", + "stateMutability": "view", + "payable": false, + "outputs": [{ "type": "uint256", "name": "", "internalType": "uint256" }], + "name": "earned", + "inputs": [ + { "type": "address", "name": "account", "internalType": "address" }, + { "type": "address", "name": "_rewardsToken", "internalType": "address" } + ], + "constant": true + }, + { + "type": "function", + "stateMutability": "nonpayable", + "payable": false, + "outputs": [], + "name": "exit", + "inputs": [], + "constant": false + }, + { + "type": "function", + "stateMutability": "nonpayable", + "payable": false, + "outputs": [], + "name": "getReward", + "inputs": [], + "constant": false + }, + { + "type": "function", + "stateMutability": "view", + "payable": false, + "outputs": [{ "type": "uint256", "name": "", "internalType": "uint256" }], + "name": "getRewardForDuration", + "inputs": [ + { "type": "address", "name": "_rewardsToken", "internalType": "address" } + ], + "constant": true + }, + { + "type": "function", + "stateMutability": "view", + "payable": false, + "outputs": [{ "type": "uint256", "name": "", "internalType": "uint256" }], + "name": "lastPauseTime", + "inputs": [], + "constant": true + }, + { + "type": "function", + "stateMutability": "view", + "payable": false, + "outputs": [{ "type": "uint256", "name": "", "internalType": "uint256" }], + "name": "lastTimeRewardApplicable", + "inputs": [ + { "type": "address", "name": "_rewardsToken", "internalType": "address" } + ], + "constant": true + }, + { + "type": "function", + "stateMutability": "nonpayable", + "payable": false, + "outputs": [], + "name": "nominateNewOwner", + "inputs": [ + { "type": "address", "name": "_owner", "internalType": "address" } + ], + "constant": false + }, + { + "type": "function", + "stateMutability": "view", + "payable": false, + "outputs": [{ "type": "address", "name": "", "internalType": "address" }], + "name": "nominatedOwner", + "inputs": [], + "constant": true + }, + { + "type": "function", + "stateMutability": "nonpayable", + "payable": false, + "outputs": [], + "name": "notifyRewardAmount", + "inputs": [ + { "type": "address", "name": "_rewardsToken", "internalType": "address" }, + { "type": "uint256", "name": "reward", "internalType": "uint256" } + ], + "constant": false + }, + { + "type": "function", + "stateMutability": "view", + "payable": false, + "outputs": [{ "type": "address", "name": "", "internalType": "address" }], + "name": "owner", + "inputs": [], + "constant": true + }, + { + "type": "function", + "stateMutability": "view", + "payable": false, + "outputs": [{ "type": "bool", "name": "", "internalType": "bool" }], + "name": "paused", + "inputs": [], + "constant": true + }, + { + "type": "function", + "stateMutability": "nonpayable", + "payable": false, + "outputs": [], + "name": "recoverERC20", + "inputs": [ + { "type": "address", "name": "tokenAddress", "internalType": "address" }, + { "type": "uint256", "name": "tokenAmount", "internalType": "uint256" } + ], + "constant": false + }, + { + "type": "function", + "stateMutability": "view", + "payable": false, + "outputs": [ + { + "type": "address", + "name": "rewardsDistributor", + "internalType": "address" + }, + { + "type": "uint256", + "name": "rewardsDuration", + "internalType": "uint256" + }, + { "type": "uint256", "name": "periodFinish", "internalType": "uint256" }, + { "type": "uint256", "name": "rewardRate", "internalType": "uint256" }, + { + "type": "uint256", + "name": "lastUpdateTime", + "internalType": "uint256" + }, + { + "type": "uint256", + "name": "rewardPerTokenStored", + "internalType": "uint256" + } + ], + "name": "rewardData", + "inputs": [{ "type": "address", "name": "", "internalType": "address" }], + "constant": true + }, + { + "type": "function", + "stateMutability": "view", + "payable": false, + "outputs": [{ "type": "uint256", "name": "", "internalType": "uint256" }], + "name": "rewardPerToken", + "inputs": [ + { "type": "address", "name": "_rewardsToken", "internalType": "address" } + ], + "constant": true + }, + { + "type": "function", + "stateMutability": "view", + "payable": false, + "outputs": [{ "type": "address", "name": "", "internalType": "address" }], + "name": "rewardTokens", + "inputs": [{ "type": "uint256", "name": "", "internalType": "uint256" }], + "constant": true + }, + { + "type": "function", + "stateMutability": "view", + "payable": false, + "outputs": [{ "type": "uint256", "name": "", "internalType": "uint256" }], + "name": "rewards", + "inputs": [ + { "type": "address", "name": "", "internalType": "address" }, + { "type": "address", "name": "", "internalType": "address" } + ], + "constant": true + }, + { + "type": "function", + "stateMutability": "nonpayable", + "payable": false, + "outputs": [], + "name": "setPaused", + "inputs": [{ "type": "bool", "name": "_paused", "internalType": "bool" }], + "constant": false + }, + { + "type": "function", + "stateMutability": "nonpayable", + "payable": false, + "outputs": [], + "name": "setRewardsDistributor", + "inputs": [ + { "type": "address", "name": "_rewardsToken", "internalType": "address" }, + { + "type": "address", + "name": "_rewardsDistributor", + "internalType": "address" + } + ], + "constant": false + }, + { + "type": "function", + "stateMutability": "nonpayable", + "payable": false, + "outputs": [], + "name": "setRewardsDuration", + "inputs": [ + { "type": "address", "name": "_rewardsToken", "internalType": "address" }, + { + "type": "uint256", + "name": "_rewardsDuration", + "internalType": "uint256" + } + ], + "constant": false + }, + { + "type": "function", + "stateMutability": "nonpayable", + "payable": false, + "outputs": [], + "name": "stake", + "inputs": [ + { "type": "uint256", "name": "amount", "internalType": "uint256" } + ], + "constant": false + }, + { + "type": "function", + "stateMutability": "view", + "payable": false, + "outputs": [ + { "type": "address", "name": "", "internalType": "contract IERC20" } + ], + "name": "stakingToken", + "inputs": [], + "constant": true + }, + { + "type": "function", + "stateMutability": "view", + "payable": false, + "outputs": [{ "type": "uint256", "name": "", "internalType": "uint256" }], + "name": "totalSupply", + "inputs": [], + "constant": true + }, + { + "type": "function", + "stateMutability": "view", + "payable": false, + "outputs": [{ "type": "uint256", "name": "", "internalType": "uint256" }], + "name": "userRewardPerTokenPaid", + "inputs": [ + { "type": "address", "name": "", "internalType": "address" }, + { "type": "address", "name": "", "internalType": "address" } + ], + "constant": true + }, + { + "type": "function", + "stateMutability": "nonpayable", + "payable": false, + "outputs": [], + "name": "withdraw", + "inputs": [ + { "type": "uint256", "name": "amount", "internalType": "uint256" } + ], + "constant": false + } +] diff --git a/server/src/constants/abi/SingleRewardProgram.json b/server/src/constants/abi/SingleRewardProgram.json new file mode 100644 index 000000000..292fe41a9 --- /dev/null +++ b/server/src/constants/abi/SingleRewardProgram.json @@ -0,0 +1,319 @@ +[ + { + "constant": false, + "inputs": [], + "name": "withdrawInterest", + "outputs": [], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "constant": false, + "inputs": [ + { + "name": "_amount", + "type": "uint256" + } + ], + "name": "withdrawStakeAndInterest", + "outputs": [], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "constant": false, + "inputs": [], + "name": "updateGlobalYield", + "outputs": [], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "constant": true, + "inputs": [], + "name": "interestData", + "outputs": [ + { + "name": "globalTotalStaked", + "type": "uint256" + }, + { + "name": "globalYieldPerToken", + "type": "uint256" + }, + { + "name": "lastUpdated", + "type": "uint256" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [], + "name": "vaultAddress", + "outputs": [ + { + "name": "", + "type": "address" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [], + "name": "stakingStartTime", + "outputs": [ + { + "name": "", + "type": "uint256" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [], + "name": "totalReward", + "outputs": [ + { + "name": "", + "type": "uint256" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [ + { + "name": "_staker", + "type": "address" + } + ], + "name": "getYieldData", + "outputs": [ + { + "name": "", + "type": "uint256" + }, + { + "name": "", + "type": "uint256" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": false, + "inputs": [ + { + "name": "_amount", + "type": "uint256" + } + ], + "name": "stake", + "outputs": [], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "constant": true, + "inputs": [], + "name": "stakingPeriod", + "outputs": [ + { + "name": "", + "type": "uint256" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [ + { + "name": "_staker", + "type": "address" + } + ], + "name": "getStakerData", + "outputs": [ + { + "name": "", + "type": "uint256" + }, + { + "name": "", + "type": "uint256" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [ + { + "name": "_staker", + "type": "address" + } + ], + "name": "calculateInterest", + "outputs": [ + { + "name": "", + "type": "uint256" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [ + { + "name": "_staker", + "type": "address" + } + ], + "name": "getStatsData", + "outputs": [ + { + "name": "", + "type": "uint256" + }, + { + "name": "", + "type": "uint256" + }, + { + "name": "", + "type": "uint256" + }, + { + "name": "", + "type": "uint256" + }, + { + "name": "", + "type": "uint256" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "name": "_stakeToken", + "type": "address" + }, + { + "name": "_rewardToken", + "type": "address" + }, + { + "name": "_stakingPeriod", + "type": "uint256" + }, + { + "name": "_totalRewardToBeDistributed", + "type": "uint256" + }, + { + "name": "_vaultAdd", + "type": "address" + } + ], + "payable": false, + "stateMutability": "nonpayable", + "type": "constructor" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "name": "staker", + "type": "address" + }, + { + "indexed": false, + "name": "value", + "type": "uint256" + }, + { + "indexed": false, + "name": "_globalYieldPerToken", + "type": "uint256" + } + ], + "name": "Staked", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "name": "staker", + "type": "address" + }, + { + "indexed": false, + "name": "value", + "type": "uint256" + }, + { + "indexed": false, + "name": "_globalYieldPerToken", + "type": "uint256" + } + ], + "name": "StakeWithdrawn", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "name": "staker", + "type": "address" + }, + { + "indexed": false, + "name": "_value", + "type": "uint256" + }, + { + "indexed": false, + "name": "_globalYieldPerToken", + "type": "uint256" + } + ], + "name": "InterestCollected", + "type": "event" + } +] \ No newline at end of file diff --git a/server/src/routes/api/v2/relay.js b/server/src/routes/api/v2/relay.js index 9108d1dc4..7974fa9ef 100644 --- a/server/src/routes/api/v2/relay.js +++ b/server/src/routes/api/v2/relay.js @@ -1,11 +1,13 @@ const router = require('express').Router() const taskManager = require('@services/taskManager') const auth = require('@routes/auth') +const relayParser = require('@services/wallet/relayJobParser') router.post('/', auth.required, async (req, res) => { const { appName, identifier } = req.user try { - const job = await taskManager.now('relay', { ...req.body, identifier, appName }, { isWalletJob: true }) + const relayBody = relayParser.parse(req.body) + const job = await taskManager.now('relay', { ...req.body, identifier, appName, relayBody }, { isWalletJob: true }) return res.json({ job }) } catch (err) { return res.status(400).send({ error: err.message }) diff --git a/server/src/services/wallet/relayJobParser.js b/server/src/services/wallet/relayJobParser.js new file mode 100644 index 000000000..d7fc858f0 --- /dev/null +++ b/server/src/services/wallet/relayJobParser.js @@ -0,0 +1,6 @@ +const { RelayJobParser } = require('@utils/job/relay') +const signatureStore = require('./signatureStore') + +const relayJobParser = new RelayJobParser(signatureStore) + +module.exports = relayJobParser diff --git a/server/src/services/abi/wallet.js b/server/src/services/wallet/signatureStore.js similarity index 60% rename from server/src/services/abi/wallet.js rename to server/src/services/wallet/signatureStore.js index 13ef70969..293428e42 100644 --- a/server/src/services/abi/wallet.js +++ b/server/src/services/wallet/signatureStore.js @@ -2,12 +2,14 @@ const { SignatureStore } = require('@utils/abi') const TransferManagerABI = require('@constants/abi/TransferManager') const CommunityManagerABI = require('@constants/abi/CommunityManager') const IUniswapV2Router02ABI = require('@constants/abi/IUniswapV2Router02') +const MultiRewardProgramABI = require('@constants/abi/MultiRewardProgram') +const SingleRewardProgramABI = require('@constants/abi/SingleRewardProgram') const signatureStore = new SignatureStore() signatureStore.addContract('TransferManager', TransferManagerABI) signatureStore.addContract('CommunityManager', CommunityManagerABI) signatureStore.addContract('FuseswapRouter', IUniswapV2Router02ABI) +signatureStore.addContract('MultiRewardProgram', MultiRewardProgramABI) +signatureStore.addContract('SingleRewardProgram', SingleRewardProgramABI) -module.exports = { - signatureStore -} +module.exports = signatureStore diff --git a/server/src/tasks/sqs/relay.js b/server/src/tasks/sqs/relay.js index 7e26c55f3..95fdf54de 100644 --- a/server/src/tasks/sqs/relay.js +++ b/server/src/tasks/sqs/relay.js @@ -1,18 +1,16 @@ const config = require('config') -const lodash = require('lodash') +const { get } = require('lodash') const { createNetwork } = require('@utils/web3') const { fetchTokenByCommunity } = require('@utils/graph') -const { getParamsFromMethodData } = require('@utils/abi') -const request = require('request-promise-native') const mongoose = require('mongoose') const { notifyReceiver } = require('@services/firebase') -const { sendRelay, isAllowedToRelay } = require('@utils/relay') +const { sendRelay, isAllowedToRelay } = require('@utils/jobs/relay') const web3Utils = require('web3-utils') const UserWallet = mongoose.model('UserWallet') const Community = mongoose.model('Community') const { deduceTransactionBodyForFundToken } = require('@utils/wallet/misc') -const relay = async (account, { walletAddress, communityAddress, methodName, methodData, nonce, gasPrice, gasLimit, signature, walletModule, network, identifier, appName, nextRelays, isFunderDeprecated }, job) => { +const relay = async (account, { walletAddress, communityAddress, methodName, methodData, nonce, gasPrice, gasLimit, signature, walletModule, network, identifier, appName, nextRelays, relayBody }, job) => { const networkType = network === config.get('network.foreign.name') ? 'foreign' : 'home' const { web3 } = createNetwork(networkType, account) const walletModuleABI = require(`@constants/abi/${walletModule}`) @@ -24,67 +22,48 @@ const relay = async (account, { walletAddress, communityAddress, methodName, met const walletModuleAddress = userWallet.walletModules[walletModule] const { relayingSuccess, receipt } = await sendRelay(account, { network, walletModule, walletModuleAddress, walletAddress, methodData, nonce, signature, gasPrice, gasLimit }, job) if (relayingSuccess) { - const returnValues = lodash.get(receipt, 'events.TransactionExecuted.returnValues') + const returnValues = get(receipt, 'events.TransactionExecuted.returnValues') const { wallet, signedHash } = returnValues console.log(`Relay transaction executed successfully from wallet: ${wallet}, signedHash: ${signedHash}`) if (walletModule === 'CommunityManager') { try { - const { _community: communityAddress } = getParamsFromMethodData(walletModuleABI, 'joinCommunity', methodData) + const communityAddress = get(relayBody, 'params._community') console.log(`Requesting token funding for wallet: ${wallet} and community ${communityAddress}`) - let tokenAddress, originNetwork - if (lodash.get(job.data.transactionBody, 'tokenAddress', false) && lodash.get(job.data.transactionBody, 'originNetwork', false)) { - tokenAddress = web3Utils.toChecksumAddress(lodash.get(job.data.transactionBody, 'tokenAddress')) - originNetwork = lodash.get(job.data.transactionBody, 'originNetwork') + let tokenAddress + if (get(job.data.transactionBody, 'tokenAddress', false) && get(job.data.transactionBody, 'originNetwork', false)) { + tokenAddress = web3Utils.toChecksumAddress(get(job.data.transactionBody, 'tokenAddress')) } else { const token = await fetchTokenByCommunity(communityAddress) tokenAddress = web3Utils.toChecksumAddress(token.address) - originNetwork = token.originNetwork } const { phoneNumber } = await UserWallet.findOne({ walletAddress }) const community = await Community.findOne({ communityAddress }) - const hasBonus = lodash.get(community, `plugins.joinBonus.isActive`, false) && lodash.get(community, `plugins.joinBonus.joinInfo.amount`, false) + const hasBonus = get(community, `plugins.joinBonus.isActive`, false) && get(community, `plugins.joinBonus.joinInfo.amount`, false) if (hasBonus) { - if (isFunderDeprecated) { - const taskManager = require('@services/taskManager') - const bonusType = 'join' - const bonusAmount = lodash.get(community, `plugins.${bonusType}Bonus.${bonusType}Info.amount`) - const bonusMaxTimesLimit = lodash.get(community, `${bonusType}.maxTimes`, 100) - const jobData = { phoneNumber, receiverAddress: walletAddress, identifier, tokenAddress, communityAddress, bonusType, bonusAmount, bonusMaxTimesLimit } - const { plugins } = community - const transactionBody = await deduceTransactionBodyForFundToken(plugins, jobData) - const funderJob = await taskManager.now('fundToken', { ...jobData, transactionBody }, { isWalletJob: true }) - job.set('data.funderJobId', funderJob._id) - job.save() - } else { - request.post(`${config.get('funder.urlBase')}fund/token`, { - json: true, - body: { phoneNumber, accountAddress: walletAddress, identifier, tokenAddress, originNetwork, communityAddress } - }, (err, response, body) => { - if (err) { - console.error(`Error on token funding for wallet: ${wallet}`, err) - } else if (body.error) { - console.error(`Error on token funding for wallet: ${wallet}`, body.error) - } else if (lodash.has(body, 'job._id')) { - job.set('data.funderJobId', body.job._id) - } - job.save() - }) - } + const taskManager = require('@services/taskManager') + const bonusType = 'join' + const bonusAmount = get(community, `plugins.${bonusType}Bonus.${bonusType}Info.amount`) + const bonusMaxTimesLimit = get(community, `${bonusType}.maxTimes`, 100) + const jobData = { phoneNumber, receiverAddress: walletAddress, identifier, tokenAddress, communityAddress, bonusType, bonusAmount, bonusMaxTimesLimit } + const { plugins } = community + const transactionBody = await deduceTransactionBodyForFundToken(plugins, jobData) + const funderJob = await taskManager.now('fundToken', { ...jobData, transactionBody }, { isWalletJob: true }) + job.set('data.funderJobId', funderJob._id) + job.save() } } catch (e) { console.log(`Error on token funding for wallet: ${wallet}`, e) } } else if (walletModule === 'TransferManager' && methodName === 'transferToken') { - const { _to, _amount, _token } = getParamsFromMethodData(walletModuleABI, 'transferToken', methodData) + const { _to, _amount, _token } = relayBody.params notifyReceiver({ receiverAddress: _to, tokenAddress: _token, amountInWei: _amount, communityAddress - }) - .catch(console.error) + }).catch(console.error) } if (nextRelays && nextRelays.length > 0) { @@ -95,9 +74,9 @@ const relay = async (account, { walletAddress, communityAddress, methodName, met job.save() } } else { - job.set('data.transactionBody', { ...lodash.get(job.data, 'transactionBody', {}), status: 'failed', blockNumber: lodash.get(receipt, 'blockNumber') }) + job.set('data.transactionBody', { ...get(job.data, 'transactionBody', {}), status: 'failed', blockNumber: get(receipt, 'blockNumber') }) job.save() - console.error(`Relay transaction failed from wallet: ${lodash.get(receipt, 'events.TransactionExecuted.returnValues.wallet')}, signedHash: ${lodash.get(receipt, 'events.TransactionExecuted.returnValues.signedHash')}`) + console.error(`Relay transaction failed from wallet: ${get(receipt, 'events.TransactionExecuted.returnValues.wallet')}, signedHash: ${get(receipt, 'events.TransactionExecuted.returnValues.signedHash')}`) } return receipt } diff --git a/server/src/tasks/sqs/upgrade.js b/server/src/tasks/sqs/upgrade.js index a2054919c..2d3e0a9a9 100644 --- a/server/src/tasks/sqs/upgrade.js +++ b/server/src/tasks/sqs/upgrade.js @@ -2,7 +2,7 @@ const config = require('config') const { merge, omit } = require('lodash') const { createNetwork } = require('@utils/web3') const mongoose = require('mongoose') -const { sendRelay, isAllowedToRelay } = require('@utils/relay') +const { sendRelay, isAllowedToRelay } = require('@utils/jobs/relay') const UserWallet = mongoose.model('UserWallet') const WalletUpgrade = mongoose.model('WalletUpgrade') diff --git a/server/src/utils/jobs.js b/server/src/utils/jobs/index.js similarity index 100% rename from server/src/utils/jobs.js rename to server/src/utils/jobs/index.js diff --git a/server/src/utils/relay.js b/server/src/utils/jobs/relay/index.js similarity index 97% rename from server/src/utils/relay.js rename to server/src/utils/jobs/relay/index.js index 4bd4f92d9..dd57ff985 100644 --- a/server/src/utils/relay.js +++ b/server/src/utils/jobs/relay/index.js @@ -2,6 +2,7 @@ const config = require('config') const lodash = require('lodash') const { createNetwork } = require('@utils/web3') +const RelayJobParser = require('./parser') const isAllowedToRelayForeign = (web3, walletModule, walletModuleABI, methodName, methodData) => { const allowedModules = ['TransferManager', 'DAIPointsManager'] @@ -57,5 +58,6 @@ const sendRelay = async (account, { network, walletModule, walletModuleAddress, module.exports = { sendRelay, - isAllowedToRelay + isAllowedToRelay, + RelayJobParser } diff --git a/server/src/utils/jobs/relay/parser.js b/server/src/utils/jobs/relay/parser.js new file mode 100644 index 000000000..59948d0da --- /dev/null +++ b/server/src/utils/jobs/relay/parser.js @@ -0,0 +1,28 @@ +const { MethodParser } = require('@utils/abi') + +class RelayJobParser { + constructor (signatureStore) { + this.parser = new MethodParser(signatureStore) + } + + parseJob (job) { + if (job.name !== 'relay') { + throw Error(`job ${job._id} is not a relay job but ${job.name}`) + } + const { params, contractName, methodName } = this.parser(job.data.methodData) + if (methodName !== job.data.methodName) { + console.warn(`supplied method name of ${job.data.methodName} does not match the one extracted for method data ${methodName}, job is is ${job._id}`) + } + if (contractName !== job.data.walletModule) { + console.warn(`supplied module name of ${job.data.methodName} does not match the one extracted for method data ${methodName}, job is is ${job._id}`) + } + + if (methodName === 'approveTokenAndCallContract' || methodName === 'callContract') { + const nested = this.parser(params.data) + return { params, contractName, methodName, nested } + } + return { params, contractName, methodName } + } +} + +module.exports = RelayJobParser diff --git a/server/src/utils/wallet/actions/create.js b/server/src/utils/wallet/actions/create.js index f8b36fa25..95c847b0b 100644 --- a/server/src/utils/wallet/actions/create.js +++ b/server/src/utils/wallet/actions/create.js @@ -1,13 +1,12 @@ -const { get } = require('lodash') -const { getParamsFromMethodData } = require('@utils/abi') +const { first, last } = require('lodash') const mongoose = require('mongoose') const WalletAction = mongoose.model('WalletAction') -const { formatActionData, getActionsTypes } = require('./utils') +const { formatActionData, getActionsTypes, getRelayBody } = require('./utils') -const makeCreateWalletAction = ({ name } = {}) => async (job) => { +const makeCreateWalletAction = ({ name } = {}, actionName) => async (job) => { const data = formatActionData(job.data) return new WalletAction({ - name: name || job.name, + name: name || actionName || job.name, job: mongoose.Types.ObjectId(job._id), data, communityAddress: job.communityAddress || job.data.communityAddress, @@ -19,10 +18,7 @@ const makeCreateWalletAction = ({ name } = {}) => async (job) => { const createWalletAction = makeCreateWalletAction() const handleReceiveTokens = (job) => { - const { walletModule } = job.data - const walletModuleABI = require(`@constants/abi/${walletModule}`) - const { methodData } = job.data - const { _to, _amount, _token } = getParamsFromMethodData(walletModuleABI, 'transferToken', methodData) + const { _to, _amount, _token } = getRelayBody(job).params const tokenAddress = _token.toLowerCase() const actionData = formatActionData({ ...job.data, transactionBody: { ...job.data.transactionBody, value: _amount, tokenAddress } }) return new WalletAction({ @@ -36,10 +32,7 @@ const handleReceiveTokens = (job) => { } const handleSendTokens = (job) => { - const { walletModule } = job.data - const walletModuleABI = require(`@constants/abi/${walletModule}`) - const { methodData } = job.data - const { _amount, _token, _wallet } = getParamsFromMethodData(walletModuleABI, 'transferToken', methodData) + const { _amount, _token, _wallet } = getRelayBody(job).params const tokenAddress = _token.toLowerCase() const actionData = formatActionData({ ...job.data, transactionBody: { ...job.data.transactionBody, value: _amount, tokenAddress } }) return new WalletAction({ @@ -53,12 +46,13 @@ const handleSendTokens = (job) => { } const handleSwapTokens = (job) => { - const { walletModule } = job.data - const walletModuleABI = require(`@constants/abi/${walletModule}`) - const { methodData } = job.data - const { _wallet, _token, _contract, _amount } = getParamsFromMethodData(walletModuleABI, 'approveTokenAndCallContract', methodData) - const tokenAddressIn = _token.toLowerCase() - const tokenAddressOut = get(job, 'data.txMetadata.currencyOut') + const { params } = getRelayBody(job) + const { to, amountIn, path } = params + const walletAddress = to + const value = amountIn || 0 + const { _contract } = job.data.relayBody.params + const tokenAddressIn = first(path).toLowerCase() + const tokenAddressOut = last(path).toLowerCase() return new WalletAction({ name: 'swapTokens', @@ -66,14 +60,14 @@ const handleSwapTokens = (job) => { data: { ...formatActionData(job.data), spender: _contract, - value: _amount, + value, tokenAddress: tokenAddressIn, timestamp: (Math.round(new Date().getTime() / 1000)).toString() }, tokenAddress: [tokenAddressIn, tokenAddressOut], - walletAddress: _wallet, + walletAddress, communityAddress: job.communityAddress || job.data.communityAddress - }) + }).save() } const handleFundToken = (job) => { @@ -110,7 +104,7 @@ const createActionFromJob = async (job) => { for (let action of actionTypes) { console.log(`Received action type of ${action}`) const makeActionFunc = specialActionHandlers[action] || createWalletAction - await makeActionFunc(job) + await makeActionFunc(job, action) } } catch (err) { console.error(err) diff --git a/server/src/utils/wallet/actions/utils.js b/server/src/utils/wallet/actions/utils.js index fb5db88d1..50fd317c5 100644 --- a/server/src/utils/wallet/actions/utils.js +++ b/server/src/utils/wallet/actions/utils.js @@ -18,34 +18,48 @@ const formatActionData = ({ transactionBody, txHash, bonusType, externalId, deta txHash }, identity) +const actionsMapping = { + 'CommunityManager': { + 'joinCommunity': 'joinCommunity' + }, + 'TransferManager': { + 'transferToken': ['sendTokens', 'receiveTokens'] + }, + 'FuseswapRouter': { + 'swapExactTokensForTokens': 'swapTokens', + 'swapTokensForExactTokens': 'swapTokens', + 'swapExactETHForTokens': 'swapTokens', + 'swapTokensForExactETH': 'swapTokens', + 'swapExactTokensForETH': 'swapTokens', + 'swapETHForExactTokens': 'swapTokens', + 'swapExactTokensForTokensSupportingFeeOnTransferTokens': 'swapTokens', + 'swapExactETHForTokensSupportingFeeOnTransferTokens': 'swapTokens', + 'swapExactTokensForETHSupportingFeeOnTransferTokens': 'swapTokens', + 'addLiquidity': 'addLiquidity', + 'addLiquidityETH': 'addLiquidity', + 'removeLiquidity': 'removeLiquidity', + 'removeLiquidityETH': 'removeLiquidity' + }, + 'MultiRewardProgram': { + 'stake': 'stakeLiquidity', + 'withdraw': 'withdrawLiquidity', + 'getReward': 'claimReward' + } +} + +const getRelayBody = (job) => get(job, 'data.relayBody.nested', get(job, 'data.relayBody')) + const getActionsTypes = (job) => { if (job.name !== 'relay') { return job.name } - const { walletModule, methodName } = job.data - console.log({ walletModule, methodName }) - switch (walletModule) { - case 'CommunityManager': - switch (methodName) { - case 'joinCommunity': - return 'joinCommunity' - } - break - case 'TransferManager': - switch (methodName) { - case 'transferToken': - return ['sendTokens', 'receiveTokens'] - case 'approveTokenAndCallContract': - return 'swapTokens' - case 'callContract': - return 'sendTokens' - default: - return 'sendTokens' - } - } + const { contractName, methodName } = getRelayBody(job) + console.log({ contractName, methodName }) + return get(actionsMapping, `${contractName}.${methodName}`) } module.exports = { formatActionData, - getActionsTypes + getActionsTypes, + getRelayBody } diff --git a/server/test/utils/abi.spec.js b/server/test/utils/abi.spec.js index 03032ad3e..fd0f89f0b 100644 --- a/server/test/utils/abi.spec.js +++ b/server/test/utils/abi.spec.js @@ -2,7 +2,7 @@ require('module-alias/register') const { expect } = require('chai') const { SignatureStore, MethodParser } = require('@utils/abi') -const { signatureStore } = require('@services/abi/wallet') +const signatureStore = require('@services/wallet/signatureStore') const TransferManagerABI = require('@constants/abi/TransferManager') const CommunityManagerABI = require('@constants/abi/CommunityManager') diff --git a/server/test/utils/wallet/actions/test.spec.js b/server/test/utils/wallet/actions/test.spec.js index ae11949d0..2821b6f7f 100644 --- a/server/test/utils/wallet/actions/test.spec.js +++ b/server/test/utils/wallet/actions/test.spec.js @@ -9,10 +9,15 @@ describe('getActionsTypes', () => { data: { walletModule: 'TransferManager', methodName: 'approveTokenAndCallContract', - methodData: '0x2df546f4000000000000000000000000af21fb07aed5f2fcb2664b67f1f9a9de5faf4de0000000000000000000000000eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee00000000000000000000000057520b81f4045f883fd0a16353adfa1480c86c2800000000000000000000000000000000000000000000000fb88ef7839f48000000000000000000000000000000000000000000000000000000000000000000a00000000000000000000000000000000000000000000000000000000000000000' + methodData: '0x2df546f4000000000000000000000000af21fb07aed5f2fcb2664b67f1f9a9de5faf4de0000000000000000000000000eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee00000000000000000000000057520b81f4045f883fd0a16353adfa1480c86c2800000000000000000000000000000000000000000000000fb88ef7839f48000000000000000000000000000000000000000000000000000000000000000000a00000000000000000000000000000000000000000000000000000000000000000', + relayBody: { + nested: { + contractName: 'FuseswapRouter', + methodName: 'swapTokensForExactETH' + } + } } } - console.log(getActionsTypes(job)) expect(getActionsTypes(job)).to.equal('swapTokens') }) }) From d5330c48af00fbfbfe905520100fe8da1c12b1b5 Mon Sep 17 00:00:00 2001 From: Leon Prouger Date: Fri, 3 Sep 2021 22:42:45 +0300 Subject: [PATCH 7/8] addinc action name to factory functions --- server/src/utils/wallet/actions/create.js | 36 +++++++++++++++++++---- 1 file changed, 31 insertions(+), 5 deletions(-) diff --git a/server/src/utils/wallet/actions/create.js b/server/src/utils/wallet/actions/create.js index 95c847b0b..8120f375d 100644 --- a/server/src/utils/wallet/actions/create.js +++ b/server/src/utils/wallet/actions/create.js @@ -17,12 +17,12 @@ const makeCreateWalletAction = ({ name } = {}, actionName) => async (job) => { const createWalletAction = makeCreateWalletAction() -const handleReceiveTokens = (job) => { +const handleReceiveTokens = (job, name) => { const { _to, _amount, _token } = getRelayBody(job).params const tokenAddress = _token.toLowerCase() const actionData = formatActionData({ ...job.data, transactionBody: { ...job.data.transactionBody, value: _amount, tokenAddress } }) return new WalletAction({ - name: 'receiveTokens', + name, job: mongoose.Types.ObjectId(job._id), data: actionData, tokenAddress, @@ -31,12 +31,12 @@ const handleReceiveTokens = (job) => { }).save() } -const handleSendTokens = (job) => { +const handleSendTokens = (job, name) => { const { _amount, _token, _wallet } = getRelayBody(job).params const tokenAddress = _token.toLowerCase() const actionData = formatActionData({ ...job.data, transactionBody: { ...job.data.transactionBody, value: _amount, tokenAddress } }) return new WalletAction({ - name: 'sendTokens', + name, job: mongoose.Types.ObjectId(job._id), data: actionData, walletAddress: _wallet, @@ -45,7 +45,32 @@ const handleSendTokens = (job) => { }).save() } -const handleSwapTokens = (job) => { +const handleSwapTokens = (job, name) => { + const { params } = getRelayBody(job) + const { to, amountIn, path } = params + const walletAddress = to + const value = amountIn || 0 + const { _contract } = job.data.relayBody.params + const tokenAddressIn = first(path).toLowerCase() + const tokenAddressOut = last(path).toLowerCase() + + return new WalletAction({ + name, + job: mongoose.Types.ObjectId(job._id), + data: { + ...formatActionData(job.data), + spender: _contract, + value, + tokenAddress: tokenAddressIn, + timestamp: (Math.round(new Date().getTime() / 1000)).toString() + }, + tokenAddress: [tokenAddressIn, tokenAddressOut], + walletAddress, + communityAddress: job.communityAddress || job.data.communityAddress + }).save() +} + +const handleAddLiquidity = (job) => { const { params } = getRelayBody(job) const { to, amountIn, path } = params const walletAddress = to @@ -95,6 +120,7 @@ const specialActionHandlers = { swapTokens: handleSwapTokens, setWalletOwner: handleSetWalletOwnerJob, tokenBonus: handleFundToken, + addLiquidity: handleAddLiquidity, 'fiat-deposit': makeMintDeposited } From d26901b90c6faf0f317cd3df4e201b04ee825651 Mon Sep 17 00:00:00 2001 From: Leon Prouger Date: Sun, 5 Sep 2021 12:41:47 +0300 Subject: [PATCH 8/8] Fixing Eth swaps --- server/config/default.js | 6 +- server/src/models/WalletAction.js | 2 + server/src/services/wallet/signatureStore.js | 2 - server/src/utils/wallet/actions/create.js | 223 ++++++++++++++++-- server/src/utils/wallet/actions/utils.js | 44 +--- server/test/index.js | 2 + server/test/utils/abi.spec.js | 181 +++++++++++--- server/test/utils/wallet/actions/test.spec.js | 49 +++- 8 files changed, 402 insertions(+), 107 deletions(-) create mode 100644 server/test/index.js diff --git a/server/config/default.js b/server/config/default.js index 2b21c87e5..a9fac2091 100644 --- a/server/config/default.js +++ b/server/config/default.js @@ -76,7 +76,8 @@ module.exports = { native: { decimals: 18, name: 'Fuse', - symbol: 'FUSE' + symbol: 'FUSE', + address: '0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE' }, name: 'fuse', bridgeType: 'home', @@ -136,7 +137,8 @@ module.exports = { native: { decimals: 18, name: 'Ether', - symbol: 'ETH' + symbol: 'ETH', + address: '0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE' }, contract: { options: { diff --git a/server/src/models/WalletAction.js b/server/src/models/WalletAction.js index ab65c8e6c..d9a90880d 100644 --- a/server/src/models/WalletAction.js +++ b/server/src/models/WalletAction.js @@ -7,6 +7,8 @@ const WalletActionSchema = new Schema({ communityAddress: { type: String }, walletAddress: { type: String }, tokenAddress: [ { type: String } ], + tokensIn: [ { type: Object } ], + tokensOut: [ { type: Object } ], job: { type: Schema.Types.ObjectId, ref: 'QueueJob' }, data: { type: Object, default: {} }, status: { type: String, default: 'pending' }, diff --git a/server/src/services/wallet/signatureStore.js b/server/src/services/wallet/signatureStore.js index 293428e42..0e5a02762 100644 --- a/server/src/services/wallet/signatureStore.js +++ b/server/src/services/wallet/signatureStore.js @@ -3,13 +3,11 @@ const TransferManagerABI = require('@constants/abi/TransferManager') const CommunityManagerABI = require('@constants/abi/CommunityManager') const IUniswapV2Router02ABI = require('@constants/abi/IUniswapV2Router02') const MultiRewardProgramABI = require('@constants/abi/MultiRewardProgram') -const SingleRewardProgramABI = require('@constants/abi/SingleRewardProgram') const signatureStore = new SignatureStore() signatureStore.addContract('TransferManager', TransferManagerABI) signatureStore.addContract('CommunityManager', CommunityManagerABI) signatureStore.addContract('FuseswapRouter', IUniswapV2Router02ABI) signatureStore.addContract('MultiRewardProgram', MultiRewardProgramABI) -signatureStore.addContract('SingleRewardProgram', SingleRewardProgramABI) module.exports = signatureStore diff --git a/server/src/utils/wallet/actions/create.js b/server/src/utils/wallet/actions/create.js index 8120f375d..01a47bbf4 100644 --- a/server/src/utils/wallet/actions/create.js +++ b/server/src/utils/wallet/actions/create.js @@ -1,12 +1,17 @@ -const { first, last } = require('lodash') +const config = require('config') +const { first, last, get, isFunction } = require('lodash') const mongoose = require('mongoose') const WalletAction = mongoose.model('WalletAction') -const { formatActionData, getActionsTypes, getRelayBody } = require('./utils') +const { formatActionData } = require('./utils') +const { createContract } = require('@services/web3/home') +const MultiRewardProgramABI = require('@constants/abi/MultiRewardProgram') -const makeCreateWalletAction = ({ name } = {}, actionName) => async (job) => { +const NATIVE_ADDRESS = config.get('network.home.native.address') + +const makeCreateWalletAction = ({ name } = {}) => async (job) => { const data = formatActionData(job.data) return new WalletAction({ - name: name || actionName || job.name, + name: name || job.name, job: mongoose.Types.ObjectId(job._id), data, communityAddress: job.communityAddress || job.data.communityAddress, @@ -17,79 +22,211 @@ const makeCreateWalletAction = ({ name } = {}, actionName) => async (job) => { const createWalletAction = makeCreateWalletAction() -const handleReceiveTokens = (job, name) => { +const handleReceiveTokens = (job) => { const { _to, _amount, _token } = getRelayBody(job).params const tokenAddress = _token.toLowerCase() const actionData = formatActionData({ ...job.data, transactionBody: { ...job.data.transactionBody, value: _amount, tokenAddress } }) return new WalletAction({ - name, + name: 'receiveTokens', job: mongoose.Types.ObjectId(job._id), data: actionData, tokenAddress, + tokensIn: { + tokenAddress, + amount: _amount + }, walletAddress: _to, communityAddress: job.communityAddress || job.data.communityAddress }).save() } -const handleSendTokens = (job, name) => { +const handleSendTokens = (job) => { const { _amount, _token, _wallet } = getRelayBody(job).params const tokenAddress = _token.toLowerCase() const actionData = formatActionData({ ...job.data, transactionBody: { ...job.data.transactionBody, value: _amount, tokenAddress } }) return new WalletAction({ - name, + name: 'sendTokens', job: mongoose.Types.ObjectId(job._id), data: actionData, walletAddress: _wallet, tokenAddress, + tokensOut: { + tokenAddress, + amount: _amount + }, communityAddress: job.communityAddress || job.data.communityAddress }).save() } -const handleSwapTokens = (job, name) => { +const handleSwapTokens = (job) => { const { params } = getRelayBody(job) const { to, amountIn, path } = params const walletAddress = to const value = amountIn || 0 - const { _contract } = job.data.relayBody.params const tokenAddressIn = first(path).toLowerCase() const tokenAddressOut = last(path).toLowerCase() return new WalletAction({ - name, + name: 'swapTokens', job: mongoose.Types.ObjectId(job._id), data: { ...formatActionData(job.data), - spender: _contract, value, tokenAddress: tokenAddressIn, timestamp: (Math.round(new Date().getTime() / 1000)).toString() }, tokenAddress: [tokenAddressIn, tokenAddressOut], + tokensIn: { + tokenAddress: tokenAddressIn, + amount: value + }, + tokensOut: { + tokenAddress: tokenAddressOut + }, walletAddress, communityAddress: job.communityAddress || job.data.communityAddress }).save() } -const handleAddLiquidity = (job) => { +const handleSwapEthTokens = (job) => { const { params } = getRelayBody(job) - const { to, amountIn, path } = params + const { to, path, amountOutMin } = params + const { _value } = job.data.relayBody.params + const walletAddress = to + const tokenAddressOut = NATIVE_ADDRESS + const tokenAddressIn = last(path).toLowerCase() + + return new WalletAction({ + name: 'swapTokens', + job: mongoose.Types.ObjectId(job._id), + data: { + ...formatActionData(job.data), + value: _value, + tokenAddress: tokenAddressIn, + timestamp: (Math.round(new Date().getTime() / 1000)).toString() + }, + tokenAddress: [tokenAddressIn, tokenAddressOut], + tokensIn: { + tokenAddress: tokenAddressIn, + amount: amountOutMin + }, + tokensOut: { + tokenAddress: tokenAddressOut, + amount: _value + }, + walletAddress, + communityAddress: job.communityAddress || job.data.communityAddress + }).save() +} + +const handleSwapTokensEth = (job) => { + const { params } = getRelayBody(job) + const { to, path, amountOutMin } = params + const { _value } = job.data.relayBody.params const walletAddress = to - const value = amountIn || 0 - const { _contract } = job.data.relayBody.params const tokenAddressIn = first(path).toLowerCase() - const tokenAddressOut = last(path).toLowerCase() + const tokenAddressOut = NATIVE_ADDRESS return new WalletAction({ name: 'swapTokens', job: mongoose.Types.ObjectId(job._id), data: { ...formatActionData(job.data), - spender: _contract, - value, + value: _value, tokenAddress: tokenAddressIn, timestamp: (Math.round(new Date().getTime() / 1000)).toString() }, tokenAddress: [tokenAddressIn, tokenAddressOut], + tokensIn: { + tokenAddress: tokenAddressIn, + amount: _value + }, + tokensOut: { + tokenAddress: tokenAddressOut, + amount: amountOutMin + }, + walletAddress, + communityAddress: job.communityAddress || job.data.communityAddress + }).save() +} + +const handleAddLiquidity = (job) => { + const { params } = getRelayBody(job) + const { to } = params + const walletAddress = to + // for AddLiquidityETh arguments are different + const { tokenA, tokenB, amountADesired, amountBDesired } = params + + return new WalletAction({ + name: 'addLiquidity', + job: mongoose.Types.ObjectId(job._id), + data: { + ...formatActionData(job.data), + timestamp: (Math.round(new Date().getTime() / 1000)).toString() + }, + tokenAddress: [tokenA, tokenB], + tokensOut: [{ + tokenAddress: tokenA, + amount: amountADesired + }, + { + tokenAddress: tokenB, + amount: amountBDesired + }], + walletAddress, + communityAddress: job.communityAddress || job.data.communityAddress + }).save() +} + +const handleAddLiquidityEth = (job) => { + const { params } = getRelayBody(job) + const { to } = params + const walletAddress = to + const { _value } = job.data.relayBody.params + const { token, amountTokenDesired } = params + + return new WalletAction({ + name: 'addLiquidity', + job: mongoose.Types.ObjectId(job._id), + data: { + ...formatActionData(job.data), + timestamp: (Math.round(new Date().getTime() / 1000)).toString() + }, + tokenAddress: [token, NATIVE_ADDRESS], + tokensOut: [{ + tokenAddress: token, + amount: amountTokenDesired + }, + { + tokenAddress: NATIVE_ADDRESS, + amount: _value + }], + walletAddress, + communityAddress: job.communityAddress || job.data.communityAddress + }).save() +} + +const handleStake = async (job) => { + const { params } = getRelayBody(job) + const { amount } = params + const { _wallet, _contract } = job.data.relayBody.params + const stakingContract = createContract(MultiRewardProgramABI, _contract) + const tokenAddress = await stakingContract.methods.stakingToken().call() + const walletAddress = _wallet + return new WalletAction({ + name: 'stakeLiquidity', + job: mongoose.Types.ObjectId(job._id), + data: { + ...formatActionData(job.data), + value: amount, + tokenAddress, + timestamp: (Math.round(new Date().getTime() / 1000)).toString() + }, + tokenAddress: [tokenAddress], + tokensOut: { + tokenAddress: tokenAddress, + amount + }, walletAddress, communityAddress: job.communityAddress || job.data.communityAddress }).save() @@ -115,8 +252,6 @@ const handleSetWalletOwnerJob = makeCreateWalletAction({ name: 'createWallet' }) const makeMintDeposited = makeCreateWalletAction({ name: 'fiat-deposit' }) const specialActionHandlers = { - receiveTokens: handleReceiveTokens, - sendTokens: handleSendTokens, swapTokens: handleSwapTokens, setWalletOwner: handleSetWalletOwnerJob, tokenBonus: handleFundToken, @@ -124,12 +259,52 @@ const specialActionHandlers = { 'fiat-deposit': makeMintDeposited } +const actionsMapping = { + 'CommunityManager': { + 'joinCommunity': 'joinCommunity' + }, + 'TransferManager': { + 'transferToken': [handleSendTokens, handleReceiveTokens] + }, + 'FuseswapRouter': { + 'swapExactTokensForTokens': handleSwapTokens, + 'swapTokensForExactTokens': handleSwapTokens, + 'swapExactETHForTokens': handleSwapEthTokens, + 'swapTokensForExactETH': handleSwapTokensEth, + 'swapExactTokensForETH': handleSwapTokensEth, + 'swapETHForExactTokens': handleSwapEthTokens, + 'swapExactTokensForTokensSupportingFeeOnTransferTokens': handleSwapTokens, + 'swapExactETHForTokensSupportingFeeOnTransferTokens': handleSwapEthTokens, + 'swapExactTokensForETHSupportingFeeOnTransferTokens': handleSwapTokensEth, + 'addLiquidity': handleAddLiquidity, + 'addLiquidityETH': handleAddLiquidityEth, + 'removeLiquidity': 'removeLiquidity', + 'removeLiquidityETH': 'removeLiquidity' + }, + 'MultiRewardProgram': { + 'stake': handleStake, + 'withdraw': 'withdrawLiquidity', + 'getReward': 'claimReward' + } +} + +const getRelayBody = (job) => get(job, 'data.relayBody.nested', get(job, 'data.relayBody')) + +const getActionsTypes = (job) => { + if (job.name !== 'relay') { + return job.name + } + const { contractName, methodName } = getRelayBody(job) + console.log({ contractName, methodName }) + return get(actionsMapping, `${contractName}.${methodName}`) +} + const createActionFromJob = async (job) => { try { const actionTypes = [getActionsTypes(job)].flat() for (let action of actionTypes) { console.log(`Received action type of ${action}`) - const makeActionFunc = specialActionHandlers[action] || createWalletAction + const makeActionFunc = isFunction(action) ? action : specialActionHandlers[action] || createWalletAction await makeActionFunc(job, action) } } catch (err) { @@ -138,5 +313,7 @@ const createActionFromJob = async (job) => { } module.exports = { - createActionFromJob + createActionFromJob, + getRelayBody, + getActionsTypes } diff --git a/server/src/utils/wallet/actions/utils.js b/server/src/utils/wallet/actions/utils.js index 50fd317c5..dae5bf3f9 100644 --- a/server/src/utils/wallet/actions/utils.js +++ b/server/src/utils/wallet/actions/utils.js @@ -18,48 +18,6 @@ const formatActionData = ({ transactionBody, txHash, bonusType, externalId, deta txHash }, identity) -const actionsMapping = { - 'CommunityManager': { - 'joinCommunity': 'joinCommunity' - }, - 'TransferManager': { - 'transferToken': ['sendTokens', 'receiveTokens'] - }, - 'FuseswapRouter': { - 'swapExactTokensForTokens': 'swapTokens', - 'swapTokensForExactTokens': 'swapTokens', - 'swapExactETHForTokens': 'swapTokens', - 'swapTokensForExactETH': 'swapTokens', - 'swapExactTokensForETH': 'swapTokens', - 'swapETHForExactTokens': 'swapTokens', - 'swapExactTokensForTokensSupportingFeeOnTransferTokens': 'swapTokens', - 'swapExactETHForTokensSupportingFeeOnTransferTokens': 'swapTokens', - 'swapExactTokensForETHSupportingFeeOnTransferTokens': 'swapTokens', - 'addLiquidity': 'addLiquidity', - 'addLiquidityETH': 'addLiquidity', - 'removeLiquidity': 'removeLiquidity', - 'removeLiquidityETH': 'removeLiquidity' - }, - 'MultiRewardProgram': { - 'stake': 'stakeLiquidity', - 'withdraw': 'withdrawLiquidity', - 'getReward': 'claimReward' - } -} - -const getRelayBody = (job) => get(job, 'data.relayBody.nested', get(job, 'data.relayBody')) - -const getActionsTypes = (job) => { - if (job.name !== 'relay') { - return job.name - } - const { contractName, methodName } = getRelayBody(job) - console.log({ contractName, methodName }) - return get(actionsMapping, `${contractName}.${methodName}`) -} - module.exports = { - formatActionData, - getActionsTypes, - getRelayBody + formatActionData } diff --git a/server/test/index.js b/server/test/index.js new file mode 100644 index 000000000..1e0304dd7 --- /dev/null +++ b/server/test/index.js @@ -0,0 +1,2 @@ +require('module-alias/register') +require('../src/services/mongo').start() diff --git a/server/test/utils/abi.spec.js b/server/test/utils/abi.spec.js index fd0f89f0b..817491e6f 100644 --- a/server/test/utils/abi.spec.js +++ b/server/test/utils/abi.spec.js @@ -1,10 +1,9 @@ - -require('module-alias/register') const { expect } = require('chai') const { SignatureStore, MethodParser } = require('@utils/abi') const signatureStore = require('@services/wallet/signatureStore') const TransferManagerABI = require('@constants/abi/TransferManager') const CommunityManagerABI = require('@constants/abi/CommunityManager') +const { inspect } = require('util') describe('SignatureStore', () => { it('addContract should parse the contract ABI', () => { @@ -21,7 +20,7 @@ describe('SignatureStore', () => { }) describe('MethodParser', () => { - it('Should parse arguements for transferToken', () => { + it('Should parse arguments for transferToken', () => { const p = new MethodParser(signatureStore) const methodData = '0x2df546f4000000000000000000000000af21fb07aed5f2fcb2664b67f1f9a9de5faf4de0000000000000000000000000eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee00000000000000000000000057520b81f4045f883fd0a16353adfa1480c86c2800000000000000000000000000000000000000000000000fb88ef7839f48000000000000000000000000000000000000000000000000000000000000000000a00000000000000000000000000000000000000000000000000000000000000000' const { params, contractName, methodName } = p.parse(methodData) @@ -31,36 +30,156 @@ describe('MethodParser', () => { expect(params._token).to.equal('0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE') expect(params._amount).to.equal('290000000000000000000') }) + describe('#approveTokenAndCallContract', () => { + it('Should parse arguments for swapExactTokensForTokens', () => { + const p = new MethodParser(signatureStore) + const methodData = '0x09d22c8e000000000000000000000000e05e8cb522868ae468738d4a835b3de2ac1befc0000000000000000000000000249be57637d8b013ad64785404b24aebae9b098b000000000000000000000000fb76e9e7d88e308ab530330ed90e84a95257031900000000000000000000000000000000000000000000000821ab0d441498000000000000000000000000000000000000000000000000000000000000000000a0000000000000000000000000000000000000000000000000000000000000012438ed173900000000000000000000000000000000000000000000000821ab0d441498000000000000000000000000000000000000000000000000000000adba2c5806b13300000000000000000000000000000000000000000000000000000000000000a0000000000000000000000000e05e8cb522868ae468738d4a835b3de2ac1befc000000000000000000000000000000000000000000000000000000000611cb69b0000000000000000000000000000000000000000000000000000000000000003000000000000000000000000249be57637d8b013ad64785404b24aebae9b098b0000000000000000000000000be9e53fd7edac9f859882afdda116645287c629000000000000000000000000a722c13135930332eb3d749b2f0906559d2c5b9900000000000000000000000000000000000000000000000000000000' + const data = '0x38ed173900000000000000000000000000000000000000000000000821ab0d441498000000000000000000000000000000000000000000000000000000adba2c5806b13300000000000000000000000000000000000000000000000000000000000000a0000000000000000000000000e05e8cb522868ae468738d4a835b3de2ac1befc000000000000000000000000000000000000000000000000000000000611cb69b0000000000000000000000000000000000000000000000000000000000000003000000000000000000000000249be57637d8b013ad64785404b24aebae9b098b0000000000000000000000000be9e53fd7edac9f859882afdda116645287c629000000000000000000000000a722c13135930332eb3d749b2f0906559d2c5b99' + const { params, contractName, methodName } = p.parse(methodData) + expect(contractName).to.equal('TransferManager') + expect(methodName).to.equal('approveTokenAndCallContract') + expect(params._wallet).to.equal('0xE05E8cB522868ae468738D4a835B3dE2ac1Befc0') + expect(params._token).to.equal('0x249BE57637D8B013Ad64785404b24aeBaE9B098B') + expect(params._contract).to.equal('0xFB76e9E7d88E308aB530330eD90e84a952570319') + expect(params._amount).to.equal('150000000000000000000') + expect(params._data).to.equal(data) - it('Should parse arguements for approveTokenAndCallContract', () => { - const p = new MethodParser(signatureStore) - const methodData = '0x09d22c8e000000000000000000000000e05e8cb522868ae468738d4a835b3de2ac1befc0000000000000000000000000249be57637d8b013ad64785404b24aebae9b098b000000000000000000000000fb76e9e7d88e308ab530330ed90e84a95257031900000000000000000000000000000000000000000000000821ab0d441498000000000000000000000000000000000000000000000000000000000000000000a0000000000000000000000000000000000000000000000000000000000000012438ed173900000000000000000000000000000000000000000000000821ab0d441498000000000000000000000000000000000000000000000000000000adba2c5806b13300000000000000000000000000000000000000000000000000000000000000a0000000000000000000000000e05e8cb522868ae468738d4a835b3de2ac1befc000000000000000000000000000000000000000000000000000000000611cb69b0000000000000000000000000000000000000000000000000000000000000003000000000000000000000000249be57637d8b013ad64785404b24aebae9b098b0000000000000000000000000be9e53fd7edac9f859882afdda116645287c629000000000000000000000000a722c13135930332eb3d749b2f0906559d2c5b9900000000000000000000000000000000000000000000000000000000' - const data = '0x38ed173900000000000000000000000000000000000000000000000821ab0d441498000000000000000000000000000000000000000000000000000000adba2c5806b13300000000000000000000000000000000000000000000000000000000000000a0000000000000000000000000e05e8cb522868ae468738d4a835b3de2ac1befc000000000000000000000000000000000000000000000000000000000611cb69b0000000000000000000000000000000000000000000000000000000000000003000000000000000000000000249be57637d8b013ad64785404b24aebae9b098b0000000000000000000000000be9e53fd7edac9f859882afdda116645287c629000000000000000000000000a722c13135930332eb3d749b2f0906559d2c5b99' - const { params, contractName, methodName } = p.parse(methodData) - // console.log(parsed) - expect(contractName).to.equal('TransferManager') - expect(methodName).to.equal('approveTokenAndCallContract') - expect(params._wallet).to.equal('0xE05E8cB522868ae468738D4a835B3dE2ac1Befc0') - expect(params._token).to.equal('0x249BE57637D8B013Ad64785404b24aeBaE9B098B') - expect(params._contract).to.equal('0xFB76e9E7d88E308aB530330eD90e84a952570319') - expect(params._amount).to.equal('150000000000000000000') - expect(params._data).to.equal(data) + function checkInner (methodData) { + const { params, contractName, methodName } = p.parse(methodData) + console.log({ params }) + expect(contractName).to.equal('FuseswapRouter') + expect(methodName).to.equal('swapExactTokensForTokens') + expect(params.amountIn).to.equal('150000000000000000000') + expect(params.amountOutMin).to.equal('48899870589104435') + expect(params.path).to.deep.equal([ + '0x249BE57637D8B013Ad64785404b24aeBaE9B098B', + '0x0BE9e53fd7EDaC9F859882AfdDa116645287C629', + '0xa722c13135930332Eb3d749B2F0906559D2C5b99' + ]) + expect(params.to).to.equal('0xE05E8cB522868ae468738D4a835B3dE2ac1Befc0') + expect(params.deadline).to.equal('1629271707') + } + checkInner(params._data) + }) + }) + + describe('#callContract', () => { + it('Should parse arguments for swapExactETHForTokens', () => { + const p = new MethodParser(signatureStore) + const methodData = '0xfd6ac3090000000000000000000000006ba3b80490a4c69e9c5b53dac49249f953fa4dd6000000000000000000000000fb76e9e7d88e308ab530330ed90e84a9525703190000000000000000000000000000000000000000000000000de0b6b3a7640000000000000000000000000000000000000000000000000000000000000000008000000000000000000000000000000000000000000000000000000000000000e47ff36ab50000000000000000000000000000000000000000000000000127cbae9baac29800000000000000000000000000000000000000000000000000000000000000800000000000000000000000006ba3b80490a4c69e9c5b53dac49249f953fa4dd6000000000000000000000000000000000000000000000000000000006133be7900000000000000000000000000000000000000000000000000000000000000020000000000000000000000000be9e53fd7edac9f859882afdda116645287c629000000000000000000000000249be57637d8b013ad64785404b24aebae9b098b00000000000000000000000000000000000000000000000000000000' + const data = '0x7ff36ab50000000000000000000000000000000000000000000000000127cbae9baac29800000000000000000000000000000000000000000000000000000000000000800000000000000000000000006ba3b80490a4c69e9c5b53dac49249f953fa4dd6000000000000000000000000000000000000000000000000000000006133be7900000000000000000000000000000000000000000000000000000000000000020000000000000000000000000be9e53fd7edac9f859882afdda116645287c629000000000000000000000000249be57637d8b013ad64785404b24aebae9b098b' + const { params, contractName, methodName } = p.parse(methodData) + // console.log({ params, contractName, methodName }) + expect(contractName).to.equal('TransferManager') + expect(methodName).to.equal('callContract') + expect(params._wallet).to.equal('0x6BA3b80490A4C69e9c5b53daC49249F953FA4dD6') + expect(params._contract).to.equal('0xFB76e9E7d88E308aB530330eD90e84a952570319') + expect(params._value).to.equal('1000000000000000000') + expect(params._data).to.equal(data) - function checkInner (methodData) { + function checkInner (methodData) { + const { params, contractName, methodName } = p.parse(methodData) + // console.log({ params }) + expect(contractName).to.equal('FuseswapRouter') + expect(methodName).to.equal('swapExactETHForTokens') + expect(params.amountOutMin).to.equal('83259068926050968') + expect(params.path).to.deep.equal([ + '0x0BE9e53fd7EDaC9F859882AfdDa116645287C629', + '0x249BE57637D8B013Ad64785404b24aeBaE9B098B' + ]) + expect(params.to).to.equal('0x6BA3b80490A4C69e9c5b53daC49249F953FA4dD6') + expect(params.deadline).to.equal('1630781049') + console.log(inspect(p.parse(methodData))) + } + console.log(inspect(p.parse(methodData))) + checkInner(params._data) + }) + + it('Should parse arguments for addLiquidity', () => { + const p = new MethodParser(signatureStore) + const methodData = '0xfd6ac3090000000000000000000000006ba3b80490a4c69e9c5b53dac49249f953fa4dd6000000000000000000000000fb76e9e7d88e308ab530330ed90e84a952570319000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000104e8e33700000000000000000000000000a722c13135930332eb3d749b2f0906559d2c5b99000000000000000000000000249be57637d8b013ad64785404b24aebae9b098b0000000000000000000000000000000000000000000000000000006d94d84fa500000000000000000000000000000000000000000000000000038d7ea4c680000000000000000000000000000000000000000000000000000000006d0894ab68000000000000000000000000000000000000000000000000000388f27d8d30000000000000000000000000006ba3b80490a4c69e9c5b53dac49249f953fa4dd6000000000000000000000000000000000000000000000000000000006133768000000000000000000000000000000000000000000000000000000000' + const data = '0xe8e33700000000000000000000000000a722c13135930332eb3d749b2f0906559d2c5b99000000000000000000000000249be57637d8b013ad64785404b24aebae9b098b0000000000000000000000000000000000000000000000000000006d94d84fa500000000000000000000000000000000000000000000000000038d7ea4c680000000000000000000000000000000000000000000000000000000006d0894ab68000000000000000000000000000000000000000000000000000388f27d8d30000000000000000000000000006ba3b80490a4c69e9c5b53dac49249f953fa4dd60000000000000000000000000000000000000000000000000000000061337680' + const { params, contractName, methodName } = p.parse(methodData) + console.log({ params, contractName, methodName }) + expect(contractName).to.equal('TransferManager') + expect(methodName).to.equal('callContract') + expect(params._wallet).to.equal('0x6BA3b80490A4C69e9c5b53daC49249F953FA4dD6') + expect(params._contract).to.equal('0xFB76e9E7d88E308aB530330eD90e84a952570319') + expect(params._value).to.equal('0') + expect(params._data).to.equal(data) + + function checkInner (methodData) { + const { params, contractName, methodName } = p.parse(methodData) + console.log({ params }) + expect(contractName).to.equal('FuseswapRouter') + expect(methodName).to.equal('addLiquidity') + expect(params.tokenA).to.equal('0xa722c13135930332Eb3d749B2F0906559D2C5b99') + expect(params.tokenB).to.equal('0x249BE57637D8B013Ad64785404b24aeBaE9B098B') + expect(params.amountADesired).to.equal('470648639397') + expect(params.amountBDesired).to.equal('1000000000000000') + expect(params.amountAMin).to.equal('468295396200') + expect(params.amountBMin).to.equal('995000000000000') + expect(params.to).to.equal('0x6BA3b80490A4C69e9c5b53daC49249F953FA4dD6') + expect(params.deadline).to.equal('1630762624') + } + checkInner(params._data) + }) + + it('Should parse arguments for addLiquidityEth', () => { + const p = new MethodParser(signatureStore) + const methodData = '0xfd6ac3090000000000000000000000006ba3b80490a4c69e9c5b53dac49249f953fa4dd6000000000000000000000000fb76e9e7d88e308ab530330ed90e84a9525703190000000000000000000000000000000000000000000000000de0b6b3a7640000000000000000000000000000000000000000000000000000000000000000008000000000000000000000000000000000000000000000000000000000000000c4f305d719000000000000000000000000249be57637d8b013ad64785404b24aebae9b098b000000000000000000000000000000000000000000000000012993bbcb435112000000000000000000000000000000000000000000000000012816d5bc2aa8fc0000000000000000000000000000000000000000000000000dcef33a6f8380000000000000000000000000006ba3b80490a4c69e9c5b53dac49249f953fa4dd60000000000000000000000000000000000000000000000000000000061337bad00000000000000000000000000000000000000000000000000000000' + const data = '0xf305d719000000000000000000000000249be57637d8b013ad64785404b24aebae9b098b000000000000000000000000000000000000000000000000012993bbcb435112000000000000000000000000000000000000000000000000012816d5bc2aa8fc0000000000000000000000000000000000000000000000000dcef33a6f8380000000000000000000000000006ba3b80490a4c69e9c5b53dac49249f953fa4dd60000000000000000000000000000000000000000000000000000000061337bad' const { params, contractName, methodName } = p.parse(methodData) - console.log({ params }) - expect(contractName).to.equal('FuseswapRouter') - expect(methodName).to.equal('swapExactTokensForTokens') - expect(params.amountIn).to.equal('150000000000000000000') - expect(params.amountOutMin).to.equal('48899870589104435') - expect(params.path).to.deep.equal([ - '0x249BE57637D8B013Ad64785404b24aeBaE9B098B', - '0x0BE9e53fd7EDaC9F859882AfdDa116645287C629', - '0xa722c13135930332Eb3d749B2F0906559D2C5b99' - ]) - expect(params.to).to.equal('0xE05E8cB522868ae468738D4a835B3dE2ac1Befc0') - expect(params.deadline).to.equal('1629271707') - } - checkInner(params._data) + // console.log({ params, contractName, methodName }) + expect(contractName).to.equal('TransferManager') + expect(methodName).to.equal('callContract') + expect(params._wallet).to.equal('0x6BA3b80490A4C69e9c5b53daC49249F953FA4dD6') + expect(params._contract).to.equal('0xFB76e9E7d88E308aB530330eD90e84a952570319') + expect(params._value).to.equal('1000000000000000000') + expect(params._data).to.equal(data) + + function checkInner (methodData) { + const { params, contractName, methodName } = p.parse(methodData) + // console.log({ params }) + expect(contractName).to.equal('FuseswapRouter') + expect(methodName).to.equal('addLiquidityETH') + expect(params.token).to.equal('0x249BE57637D8B013Ad64785404b24aeBaE9B098B') + expect(params.amountTokenDesired).to.equal('83760502861418770') + expect(params.amountTokenMin).to.equal('83341700347111676') + expect(params.amountETHMin).to.equal('995000000000000000') + expect(params.to).to.equal('0x6BA3b80490A4C69e9c5b53daC49249F953FA4dD6') + expect(params.deadline).to.equal('1630763949') + } + checkInner(params._data) + }) + + describe('#MultiRewards', () => { + it('Should parse arguments for stake', () => { + const p = new MethodParser(signatureStore) + const methodData = '0xfd6ac3090000000000000000000000006ba3b80490a4c69e9c5b53dac49249f953fa4dd6000000000000000000000000076bdea1fd4695bdef9ba4579463bc4b3c97d645000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000024a694fc3a00000000000000000000000000000000000000000000000009d2a263e6c9eed800000000000000000000000000000000000000000000000000000000' + const data = '0xa694fc3a00000000000000000000000000000000000000000000000009d2a263e6c9eed8' + const { params, contractName, methodName } = p.parse(methodData) + console.log({ params, contractName, methodName }) + expect(contractName).to.equal('TransferManager') + expect(methodName).to.equal('callContract') + expect(params._wallet).to.equal('0x6BA3b80490A4C69e9c5b53daC49249F953FA4dD6') + expect(params._contract).to.equal('0x076BDeA1fD4695BDEF9Ba4579463BC4B3C97d645') + expect(params._value).to.equal('0') + expect(params._data).to.equal(data) + + function checkInner (methodData) { + const { params, contractName, methodName } = p.parse(methodData) + console.log({ params }) + expect(contractName).to.equal('MultiRewardProgram') + expect(methodName).to.equal('stake') + expect(params.amount).to.equal('707806641408044760') + } + console.log(inspect(p.parse(methodData))) + checkInner(params._data) + }) + }) }) }) + +// approve (callContract) +// 0xfd6ac3090000000000000000000000006ba3b80490a4c69e9c5b53dac49249f953fa4dd6000000000000000000000000a722c13135930332eb3d749b2f0906559d2c5b99000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000044095ea7b3000000000000000000000000fb76e9e7d88e308ab530330ed90e84a952570319ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00000000000000000000000000000000000000000000000000000000 \ No newline at end of file diff --git a/server/test/utils/wallet/actions/test.spec.js b/server/test/utils/wallet/actions/test.spec.js index 2821b6f7f..d77b929e6 100644 --- a/server/test/utils/wallet/actions/test.spec.js +++ b/server/test/utils/wallet/actions/test.spec.js @@ -1,23 +1,60 @@ require('module-alias/register') const { expect } = require('chai') -const { getActionsTypes } = require('@utils/wallet/actions/utils') +const { + getActionsTypes +} = require('@utils/wallet/actions/create') describe('getActionsTypes', () => { - it('swapTokens type', () => { + it('handleSwapExactETHForTokens type', async () => { const job = { name: 'relay', data: { walletModule: 'TransferManager', methodName: 'approveTokenAndCallContract', - methodData: '0x2df546f4000000000000000000000000af21fb07aed5f2fcb2664b67f1f9a9de5faf4de0000000000000000000000000eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee00000000000000000000000057520b81f4045f883fd0a16353adfa1480c86c2800000000000000000000000000000000000000000000000fb88ef7839f48000000000000000000000000000000000000000000000000000000000000000000a00000000000000000000000000000000000000000000000000000000000000000', + methodData: + '0x2df546f4000000000000000000000000af21fb07aed5f2fcb2664b67f1f9a9de5faf4de0000000000000000000000000eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee00000000000000000000000057520b81f4045f883fd0a16353adfa1480c86c2800000000000000000000000000000000000000000000000fb88ef7839f48000000000000000000000000000000000000000000000000000000000000000000a00000000000000000000000000000000000000000000000000000000000000000', relayBody: { nested: { + params: { + amountOutMin: '83259068926050968', + path: [ + '0x0BE9e53fd7EDaC9F859882AfdDa116645287C629', + '0x249BE57637D8B013Ad64785404b24aeBaE9B098B' + ], + to: '0x6BA3b80490A4C69e9c5b53daC49249F953FA4dD6', + deadline: '1630781049' + }, contractName: 'FuseswapRouter', - methodName: 'swapTokensForExactETH' - } + methodName: 'swapExactETHForTokens' + }, + params: { + __length__: 4, + _wallet: '0x6BA3b80490A4C69e9c5b53daC49249F953FA4dD6', + _contract: '0xFB76e9E7d88E308aB530330eD90e84a952570319', + _value: '1000000000000000000', + _data: + '0x7ff36ab50000000000000000000000000000000000000000000000000127cbae9baac29800000000000000000000000000000000000000000000000000000000000000800000000000000000000000006ba3b80490a4c69e9c5b53dac49249f953fa4dd6000000000000000000000000000000000000000000000000000000006133be7900000000000000000000000000000000000000000000000000000000000000020000000000000000000000000be9e53fd7edac9f859882afdda116645287c629000000000000000000000000249be57637d8b013ad64785404b24aebae9b098b' + }, + contractName: 'TransferManager', + methodName: 'callContract' } } } - expect(getActionsTypes(job)).to.equal('swapTokens') + const actionFunc = getActionsTypes(job) + expect(actionFunc.name).to.equal('handleSwapEthTokens') + const action = await actionFunc(job) + + expect(action.name).to.equal('swapTokens') + expect(action.tokenAddress).to.deep.equal([ + '0x249be57637d8b013ad64785404b24aebae9b098b', + '0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE' + ]) + expect(action.tokensIn).to.deep.equal([ + { + tokenAddress: '0x249be57637d8b013ad64785404b24aebae9b098b', + amount: '83259068926050968' + } + ]) + console.log({ action }) }) })