-
Notifications
You must be signed in to change notification settings - Fork 13
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Ben
authored
Mar 9, 2022
1 parent
4762289
commit d37059a
Showing
28 changed files
with
434 additions
and
194 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file was deleted.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,37 +1,40 @@ | ||
import { setTimeout as delay } from 'timers/promises'; | ||
import { getNetworkConfigByType } from 'auctions-core/src/constants/NETWORKS'; | ||
import { createSigner, setSigner } from 'auctions-core/src/signer'; | ||
import { getNewAuctions } from './auctions'; | ||
import { getAllAuctions, getNewAuctionsFromActiveAuctions } from './auctions'; | ||
import notify from './notify'; | ||
import { setupWallet } from './authorizations'; | ||
import participate, { setupKeeper } from './keeper'; | ||
import { ETHEREUM_NETWORK } from './variables'; | ||
import { setupTwitter } from './twitter'; | ||
|
||
const ETHEREUM_NETWORK = process.env.ETHEREUM_NETWORK || 'kovan'; | ||
const WALLET_PRIVATE_KEY = process.env.WALLET_PRIVATE_KEY; | ||
const DEFAULT_REFETCH_INTERVAL = 60 * 1000; | ||
const SETUP_DELAY = 3 * 1000; | ||
const REFETCH_INTERVAL = parseInt(process.env.REFETCH_INTERVAL ?? '') || DEFAULT_REFETCH_INTERVAL; | ||
let refetchIntervalId: ReturnType<typeof setTimeout> | undefined; | ||
|
||
const loop = async function (): Promise<void> { | ||
if (refetchIntervalId) { | ||
clearInterval(refetchIntervalId); | ||
} | ||
try { | ||
(await getNewAuctions(ETHEREUM_NETWORK)).map(notify); | ||
const activeAuctions = await getAllAuctions(ETHEREUM_NETWORK); | ||
if (activeAuctions.length === 0) { | ||
return; | ||
} | ||
const newAuctions = getNewAuctionsFromActiveAuctions(activeAuctions); | ||
newAuctions.map(notify); | ||
activeAuctions.map(participate); | ||
} catch (error) { | ||
console.error('loop error:', error); | ||
} finally { | ||
refetchIntervalId = setTimeout(loop, REFETCH_INTERVAL); | ||
} | ||
}; | ||
|
||
const setup = async function (): Promise<void> { | ||
const start = async function (): Promise<void> { | ||
await delay(SETUP_DELAY); | ||
getNetworkConfigByType(ETHEREUM_NETWORK); | ||
if (WALLET_PRIVATE_KEY) { | ||
setSigner(ETHEREUM_NETWORK, createSigner(ETHEREUM_NETWORK, WALLET_PRIVATE_KEY)); | ||
await setupWallet(ETHEREUM_NETWORK); | ||
} | ||
await loop(); | ||
await setupTwitter(); | ||
await setupKeeper(); | ||
loop(); | ||
setInterval(loop, REFETCH_INTERVAL); | ||
}; | ||
|
||
setup().catch(error => { | ||
start().catch(error => { | ||
throw error; | ||
}); | ||
|
||
export default function () {} // required by nuxt |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,151 @@ | ||
import { AuctionInitialInfo } from 'auctions-core/src/types'; | ||
import getSigner, { createSigner, setSigner } from 'auctions-core/src/signer'; | ||
import { bidOnTheAuction, enrichAuction } from 'auctions-core/src/auctions'; | ||
import { | ||
authorizeCollateral, | ||
authorizeWallet, | ||
getCollateralAuthorizationStatus, | ||
getWalletAuthorizationStatus, | ||
} from 'auctions-core/src/authorizations'; | ||
import { ETHEREUM_NETWORK, KEEPER_MINIMUM_NET_PROFIT_DAI, KEEPER_WALLET_PRIVATE_KEY } from './variables'; | ||
|
||
let isSetupCompleted = false; | ||
const currentlyExecutedAuctions = new Set(); | ||
|
||
export const setupKeeper = async function () { | ||
if (!KEEPER_WALLET_PRIVATE_KEY) { | ||
console.warn('keeper: KEEPER_WALLET_PRIVATE_KEY variable is not set, keeper will not run'); | ||
return; | ||
} | ||
if (Number.isNaN(KEEPER_MINIMUM_NET_PROFIT_DAI)) { | ||
console.warn('keeper: KEEPER_MINIMUM_NET_PROFIT_DAI is not set or not a number, keeper will not run'); | ||
return; | ||
} | ||
try { | ||
setSigner(ETHEREUM_NETWORK, createSigner(ETHEREUM_NETWORK, KEEPER_WALLET_PRIVATE_KEY)); | ||
const signer = await getSigner(ETHEREUM_NETWORK); | ||
const address = await signer.getAddress(); | ||
isSetupCompleted = true; | ||
console.info( | ||
`keeper: setup complete: using wallet "${address}", looking for minimum clear profit of "${KEEPER_MINIMUM_NET_PROFIT_DAI}" DAI` | ||
); | ||
} catch (error) { | ||
console.warn('keeper: setup error, keeper will not run, please check that KEEPER_WALLET_PRIVATE_KEY is valid'); | ||
} | ||
}; | ||
|
||
const checkAndParticipateIfPossible = async function (auction: AuctionInitialInfo) { | ||
// check if setupKeeper hasn't run | ||
if (!isSetupCompleted) { | ||
return; | ||
} | ||
|
||
const signer = await getSigner(ETHEREUM_NETWORK); | ||
|
||
// enrich the auction with more numbers | ||
const auctionTransaction = await enrichAuction(ETHEREUM_NETWORK, auction); | ||
|
||
// check if auction became inactive or finished | ||
if (auctionTransaction.isFinished) { | ||
console.info(`keeper: auction "${auction.id}" has already finished`); | ||
return; | ||
} | ||
if (!auctionTransaction.isActive) { | ||
console.info(`keeper: auction "${auction.id}" is inactive`); | ||
return; | ||
} | ||
|
||
// check auction's profit | ||
if (!auctionTransaction.transactionGrossProfit || auctionTransaction.transactionGrossProfit.isLessThan(0)) { | ||
if (auctionTransaction.transactionGrossProfit) { | ||
const profit = `${auctionTransaction.transactionGrossProfit.toFixed(0)} DAI`; | ||
console.info(`keeper: auction "${auction.id}" is not yet profitable (current profit: ${profit})`); | ||
} else { | ||
console.info(`keeper: auction "${auction.id}" is not tradable`); | ||
} | ||
return; | ||
} | ||
|
||
// check auction's clear profit – profit without transaction fees | ||
if ( | ||
auctionTransaction.transactionNetProfit && | ||
auctionTransaction.transactionNetProfit.toNumber() < KEEPER_MINIMUM_NET_PROFIT_DAI | ||
) { | ||
console.info( | ||
`keeper: auction "${ | ||
auction.id | ||
}" clear profit is smaller than min profit (${auctionTransaction.transactionNetProfit.toFixed( | ||
0 | ||
)} < ${KEEPER_MINIMUM_NET_PROFIT_DAI})` | ||
); | ||
return; | ||
} else { | ||
console.info( | ||
`keeper: auction "${auction.id}" clear profit is ${auctionTransaction.transactionNetProfit.toFixed( | ||
0 | ||
)} DAI after transaction fees, moving on to the execution` | ||
); | ||
} | ||
|
||
// get wallet authorization status | ||
const walletAddress = await signer.getAddress(); | ||
const isWalletAuth = await getWalletAuthorizationStatus(ETHEREUM_NETWORK, walletAddress); | ||
|
||
// try to authorize the wallet then return | ||
if (!isWalletAuth) { | ||
console.info(`keeper: wallet "${walletAddress}" has not been authorized yet. Attempting authorization now...`); | ||
const transactionHash = await authorizeWallet(ETHEREUM_NETWORK, false); | ||
console.info(`keeper: wallet "${walletAddress}" successfully authorized via "${transactionHash}" transaction`); | ||
await checkAndParticipateIfPossible(auction); | ||
return; | ||
} | ||
|
||
// get collateral authorization status | ||
const isCollateralAuth = await getCollateralAuthorizationStatus( | ||
ETHEREUM_NETWORK, | ||
auctionTransaction.collateralType, | ||
walletAddress | ||
); | ||
|
||
// try to authorize the collateral then return | ||
if (!isCollateralAuth) { | ||
console.info( | ||
`keeper: collateral "${auctionTransaction.collateralType}" has not been authorized on wallet "${walletAddress}" yet. Attempting authorization now...` | ||
); | ||
const collateralTransactionHash = await authorizeCollateral( | ||
ETHEREUM_NETWORK, | ||
auctionTransaction.collateralType, | ||
false | ||
); | ||
console.info( | ||
`keeper: collateral "${auctionTransaction.collateralType}" successfully authorized on wallet "${walletAddress}" via "${collateralTransactionHash}" transaction` | ||
); | ||
await checkAndParticipateIfPossible(auction); | ||
return; | ||
} | ||
|
||
// bid on the Auction | ||
console.info(`keeper: auction "${auctionTransaction.id}": attempting swap execution`); | ||
const bidHash = await bidOnTheAuction(ETHEREUM_NETWORK, auctionTransaction, walletAddress); | ||
console.info(`keeper: auction "${auctionTransaction.id}" was succesfully executed via "${bidHash}" transaction`); | ||
}; | ||
|
||
const participate = async function (auction: AuctionInitialInfo) { | ||
// check if this auction is currently executed to avoid double execution | ||
if (currentlyExecutedAuctions.has(auction.id)) { | ||
return; | ||
} | ||
currentlyExecutedAuctions.add(auction.id); | ||
|
||
// execute | ||
try { | ||
await checkAndParticipateIfPossible(auction); | ||
} catch (error) { | ||
console.error(`keeper: unexpected error: ${(error instanceof Error && error.message) || 'unknown'}`); | ||
} | ||
|
||
// clear pool of currently executed auctions | ||
currentlyExecutedAuctions.delete(auction.id); | ||
}; | ||
|
||
export default participate; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,3 @@ | ||
export const ETHEREUM_NETWORK = process.env.ETHEREUM_NETWORK || 'kovan'; | ||
export const KEEPER_MINIMUM_NET_PROFIT_DAI = parseInt(process.env.KEEPER_MINIMUM_NET_PROFIT_DAI || ''); | ||
export const KEEPER_WALLET_PRIVATE_KEY = process.env.KEEPER_WALLET_PRIVATE_KEY; |
Oops, something went wrong.