Skip to content

Commit

Permalink
Merge pull request #5 from LI-YONG-QI/feat/cmd
Browse files Browse the repository at this point in the history
Feat/cmd
  • Loading branch information
LI-YONG-QI authored May 14, 2024
2 parents cfc33f5 + f31c639 commit e91a7ec
Show file tree
Hide file tree
Showing 36 changed files with 2,391 additions and 154 deletions.
23 changes: 23 additions & 0 deletions .github/workflows/publish.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
name: "publish package to npm"

on:
push:
branches: ["main"]
pull_request:
branches: ["main"]

jobs:
publish:
runs-on: ubuntu-latest
steps:
- name: checkout
uses: actions/checkout@v2
- name: node
uses: actions/setup-node@v2
with:
node-version: 16
registry-url: https://registry.npmjs.org
- name: publish
run: npm publish --access public
env:
NODE_AUTH_TOKEN: ${{secrets.NPM_AUTH_TOKEN}}
58 changes: 22 additions & 36 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,57 +5,44 @@ This is a script for automatic interaction with the onchain protocol by setting
# Supported protocols

1. Base AAVE
- deposit 0.001 ETH -> approve -> withdraw
- Deposit ETH -> approve -> withdraw
2. Base Uniswap
- swap DAI <-> 0.0001 ETH
- Swap DAI <-> ETH

# How to start ?
# Getting started

Two options for starting the bot

1. Node.js
2. Docker

## Prerequisites

Create `.env` file with `.env.example`

You can change default value of variables in `.env.example` to `.env` if you need

## Docker

1. Build image
1. Install CLI globally

```bash
npm run docker:build
npm install -g airdrop-bot
```

2. Run container
2. Check version

```bash
npm run docker:run
airdrop-bot -v
# 0.0.1
```

3. Check docker logs
3. Interact protocols (AAVE / Uniswap)

```bash
npm run docker:logs
```

## Node.js

1. Install packages
For example, interact aave by 0.01 ETH and setup delay 10 minutes

```bash
yarn
```
# set your private key to environment variables
export PK=<your-private-key>

2. Run app
# check private key
echo $PK
```

```bash
npm run app
# execute AAVE
airdrop-bot aave -c "0 */10 * * * *" -p $PK --delay 10 --amount 0.01
```

Notice: cron job expression is from **second** not minute

---

If successfully started, the following information should appear.
Expand All @@ -65,15 +52,14 @@ If successfully started, the following information should appear.
> ts-node ./app.ts

Starting app ...
Mode <$MODE>
Time <$CRONJOB> | Delay <$DELAY> minutes
Time 0 */10 * * * * | Delay 10 minutes
Account <Public address from $PK>
```

Print below information when starting a new round

- Trigger start/end time (yime zone: Asia/Taipei)
- Random delay seconds (limit base on `$DELAY` variable in `.env`)
- Trigger start / end time (time zone: Asia/Taipei)
- Random delay seconds (limit base on `--delay` command option)
- Each txs hash and status (retry sent tx if status is `reverted`)

```bash
Expand Down
19 changes: 19 additions & 0 deletions dist/commands/aave.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.aave = void 0;
const viem_1 = require("viem");
const commander_1 = require("commander");
const protocol_1 = require("../types/protocol");
const aave_1 = require("../utils/aave");
const cron_1 = require("../libs/cron");
exports.aave = new commander_1.Command("aave");
exports.aave
.description("Start the aave protocol")
.option("-p, --pk <private key>", "private key of signer")
.option(" --amount <amount>", "amount of ETH")
.option("-d, --delay <delay time>", "delay in minutes")
.option("-c, --cronjob <cron job...>", 'cron job expression i.e. "*/5 * * * *"')
.action((options) => {
const aaveProtocol = new protocol_1.Protocol(aave_1.aave, options.pk, (0, viem_1.parseEther)(options.amount), (0, cron_1.parseCronJob)(options.cronjob), Number(options.delay));
aaveProtocol.execute();
});
19 changes: 19 additions & 0 deletions dist/commands/uniswap.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.uniswap = void 0;
const viem_1 = require("viem");
const commander_1 = require("commander");
const protocol_1 = require("../types/protocol");
const uniswap_1 = require("../utils/uniswap");
const cron_1 = require("../libs/cron");
exports.uniswap = new commander_1.Command("uniswap");
exports.uniswap
.description("Start the uniswap protocol")
.option("-p, --pk <private key>", "private key of signer")
.option(" --amount <amount>", "amount of ETH")
.option("-d, --delay <delay time>", "delay in minutes")
.option("-c, --cronjob <cron job...>", 'cron job expression (every five seconds i.e. "*/5 * * * *")')
.action((options) => {
const uniswapProtocol = new protocol_1.Protocol(uniswap_1.uniswap, options.pk, (0, viem_1.parseEther)(options.amount), (0, cron_1.parseCronJob)(options.cronjob), Number(options.delay));
uniswapProtocol.execute();
});
18 changes: 18 additions & 0 deletions dist/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
#!/usr/bin/env node
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.program = void 0;
const commander_1 = require("commander");
const figlet_1 = require("figlet");
const aave_1 = require("./commands/aave");
const uniswap_1 = require("./commands/uniswap");
console.log((0, figlet_1.textSync)("BOT"));
exports.program = new commander_1.Command();
// Main
exports.program
.version("0.0.1", "-v, --versions", "output the current version")
.description("A simple airdrop bot")
.addCommand(uniswap_1.uniswap)
.addCommand(aave_1.aave)
.showHelpAfterError("(add --help for additional information)");
exports.program.parse();
40 changes: 40 additions & 0 deletions dist/libs/cron.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
"use strict";
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
return new (P || (P = Promise))(function (resolve, reject) {
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
step((generator = generator.apply(thisArg, _arguments || [])).next());
});
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.parseCronJob = exports.createConfig = void 0;
const time_1 = require("./time");
function createConfig(interaction, cronTime, delay) {
return {
cronTime: cronTime,
onTick: function () {
return __awaiter(this, void 0, void 0, function* () {
console.log("Start new round");
yield (0, time_1.randomDelay)(delay);
yield interaction();
console.log(`End time ${(0, time_1.getTime)()}`);
console.log("=============================");
});
},
start: true,
timeZone: "Asia/Taipei",
};
}
exports.createConfig = createConfig;
function parseCronJob(cronJob) {
const parseResult = cronJob.map((job) => {
if (job.includes('"')) {
return job.replace('"', "");
}
return job;
});
return parseResult.join(" ");
}
exports.parseCronJob = parseCronJob;
39 changes: 39 additions & 0 deletions dist/libs/time.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
"use strict";
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
return new (P || (P = Promise))(function (resolve, reject) {
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
step((generator = generator.apply(thisArg, _arguments || [])).next());
});
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.delay = exports.randomDelay = exports.getTime = void 0;
function getTime() {
const date = new Date(Date.now());
const formatStakeTime = date.toLocaleString("zh-TW", {
timeZone: "Asia/Taipei",
});
return formatStakeTime;
}
exports.getTime = getTime;
function randomDelay(limit) {
return __awaiter(this, void 0, void 0, function* () {
const MINUTES = 60; // 60 seconds
const LIMIT = Number(limit) * MINUTES; // 最多不超過延遲 $DELAY 分鐘
const rand = Math.floor(Math.random() * LIMIT);
console.log(`Start time: ${getTime()} | Delay ${rand} seconds`);
yield delay(rand * 1000);
});
}
exports.randomDelay = randomDelay;
function delay(ms) {
return __awaiter(this, void 0, void 0, function* () {
return new Promise(function (resolve) {
setTimeout(resolve, ms);
});
});
}
exports.delay = delay;
// -c "*/5 * * * * *" -d 0 -p $PK --amount 0.01
30 changes: 30 additions & 0 deletions dist/libs/transaction.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
"use strict";
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
return new (P || (P = Promise))(function (resolve, reject) {
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
step((generator = generator.apply(thisArg, _arguments || [])).next());
});
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.sendTransaction = void 0;
const public_1 = require("../utils/clients/public");
function sendTransaction(request, signer) {
return __awaiter(this, void 0, void 0, function* () {
while (1) {
const hash = yield signer.writeContract(request);
const transaction = yield public_1.PUBLIC_CLIENT.waitForTransactionReceipt({
confirmations: 5,
hash,
pollingInterval: 12000,
});
console.log(`Tx Hash: ${transaction.transactionHash} - ${transaction.status}`);
if (transaction.status === "success")
break;
console.log("Reverted !! Retrying...");
}
});
}
exports.sendTransaction = sendTransaction;
36 changes: 36 additions & 0 deletions dist/types/protocol.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.Protocol = void 0;
const cron_1 = require("cron");
const viem_1 = require("viem");
const accounts_1 = require("viem/accounts");
const cron_2 = require("../libs/cron");
const config_1 = require("../utils/clients/config");
class Protocol {
constructor(interaction, privateKey, amount, cronTime = "* * * * * *", delay = 0) {
this.amount = amount;
this.cronTime = cronTime;
this.delay = delay;
const _signer = this.setSigner(privateKey);
const _interaction = interaction.bind(null, _signer, amount);
const config = (0, cron_2.createConfig)(_interaction, cronTime, delay);
this.cron = new cron_1.CronJob(config.cronTime, config.onTick, null, config.start, config.timeZone);
}
execute() {
var _a;
console.log(`Starting app ...`);
console.log(`Time ${this.cronTime} | Delay ${this.delay} minutes`);
console.log(`Account ${(_a = this.signer.account) === null || _a === void 0 ? void 0 : _a.address}`);
this.cron.start();
}
getSigner() {
return this.signer;
}
setSigner(privateKey) {
const account = (0, accounts_1.privateKeyToAccount)(privateKey);
const _signer = (0, viem_1.createWalletClient)(Object.assign({ account }, config_1.CONFIG));
this.signer = _signer;
return _signer;
}
}
exports.Protocol = Protocol;
42 changes: 42 additions & 0 deletions dist/utils/aave.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
"use strict";
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
return new (P || (P = Promise))(function (resolve, reject) {
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
step((generator = generator.apply(thisArg, _arguments || [])).next());
});
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.aave = void 0;
const viem_1 = require("viem");
const transaction_1 = require("../libs/transaction");
const aave_1 = require("./contracts/aave");
function deposit(signer, amount) {
return __awaiter(this, void 0, void 0, function* () {
const { account } = signer;
const { request } = yield aave_1.AAVE.simulate.depositETH([viem_1.zeroAddress, account.address, 0], {
value: amount,
account,
});
console.log("Deposit...");
yield (0, transaction_1.sendTransaction)(request, signer);
});
}
function withdraw(signer, amount) {
return __awaiter(this, void 0, void 0, function* () {
const { account } = signer;
const { request: approveReq } = yield aave_1.WETH.simulate.approve([aave_1.AAVE.address, amount], { account: account });
console.log("Approving...");
yield (0, transaction_1.sendTransaction)(approveReq, signer);
const { request: withdrawReq } = yield aave_1.AAVE.simulate.withdrawETH([viem_1.zeroAddress, amount, account.address], { account: account });
console.log("Withdraw...");
yield (0, transaction_1.sendTransaction)(withdrawReq, signer);
});
}
const aave = (_signer, amount) => __awaiter(void 0, void 0, void 0, function* () {
yield deposit(_signer, amount);
yield withdraw(_signer, amount);
});
exports.aave = aave;
Loading

0 comments on commit e91a7ec

Please sign in to comment.