-
Notifications
You must be signed in to change notification settings - Fork 5
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Airdrop utility based on the disperse protocol (#680)
* Create placeholder * Add readme * Delete products/airdrop/placeholder * Create placeholder * Create placeholder * Add scripts and example csv * Add disperse contract * Delete products/airdrop/contracts/placeholder * Delete products/airdrop/scripts/placeholder * (fix): airdrop formatting --------- Co-authored-by: Mauro Medda <[email protected]>
- Loading branch information
1 parent
d5f1cb3
commit 0e5d254
Showing
7 changed files
with
176 additions
and
14 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
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -2,6 +2,8 @@ | |
*logs | ||
*actions | ||
*notifications | ||
*tools | ||
plugins | ||
user_trunk.yaml | ||
user.yaml | ||
tmp |
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,28 +1,34 @@ | ||
version: 0.1 | ||
cli: | ||
version: 1.5.1 | ||
version: 1.22.0 | ||
plugins: | ||
sources: | ||
- id: trunk | ||
ref: v0.0.11 | ||
ref: v1.2.1 | ||
uri: https://github.com/trunk-io/plugins | ||
|
||
lint: | ||
enabled: | ||
- [email protected] | ||
- [email protected] | ||
- [email protected] | ||
- [email protected] | ||
- [email protected] | ||
- [email protected] | ||
- [email protected] | ||
- [email protected] | ||
- [email protected] | ||
- eslint@8.35.0 | ||
- buildifier@6.0.1 | ||
- eslint@8.57.0 | ||
- buildifier@7.1.1 | ||
- git-diff-check | ||
- markdownlint@0.33.0 | ||
- prettier@2.8.4 | ||
- shfmt@3.5.0 | ||
- svgo@3.0.2 | ||
- gitleaks@8.16.0 | ||
- flake8@6.0.0 | ||
- isort@5.12.0 | ||
- black@23.1.0 | ||
- rustfmt@1.61.0 | ||
- markdownlint@0.40.0 | ||
- prettier@3.2.5 | ||
- shfmt@3.6.0 | ||
- svgo@3.2.0 | ||
- gitleaks@8.18.2 | ||
- flake8@7.0.0 | ||
- isort@5.13.2 | ||
- black@24.4.2 | ||
- rustfmt@1.65.0 | ||
# - [email protected] - removed for being over-prissy and wanting tab indents - rrw 2023-04-25 | ||
ignore: | ||
- linters: [gitleaks] | ||
|
@@ -46,6 +52,7 @@ lint: | |
- products/bridge/bridge-web | ||
- linters: [ALL] | ||
paths: | ||
- products/airdrop/** | ||
- products/developer-portal/Dockerfile # Annoying, but trunk is now very opinionated on how you write dockerfiles. | ||
- products/developer-portal/zq2/docs/js/** # trunk does not know this is looking for metamask in the user's browser. | ||
- products/developer-portal/zq2/docs/index.md # contains legitimate empty links | ||
|
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,47 @@ | ||
# Airdrop Utility | ||
|
||
The `Disperse` contract and script in this repo provide a handy utility to automate batched transfers to a large number of accounts. It's useful for airdrops and other events where hundreds or tousands of accounts have to be sent an individual amount of a token. The contract supports the native token as well as `ERC20` tokens, but the script can currently handle only `ERC20` tokens. | ||
|
||
The contract in `contracts/Disperse.sol` is an updated version of the original contract implemented by the [Disperse Protocol](https://disperse.app/disperse.pdf) ported to Solidity 0.8.20. The original contract is deployed and actively used on the Ethereum mainnet: https://etherscan.io/address/0xD152f549545093347A162Dce210e7293f1452150. | ||
|
||
## Installation | ||
|
||
Install [Hardhat](https://hardhat.org/hardhat-runner/docs/getting-started#installation) and configure your network(s) and account(s) in `hardhat.config.js`. | ||
|
||
There are already existing deployments on the following networks: | ||
|
||
| Network | `Disperse` contract address | | ||
| ----------------- | -------------------------------------------- | | ||
| Zilliqa 1 mainnet | `0x8Cc17F9eA46cD1A98EbB1Dd5495067e3095956aA` | | ||
| Zilliqa 1 testnet | `0x38048F4B71a87a31d21C86FF373a91d1E401bea5` | | ||
|
||
Deploy a new `Disperse` contract only if it does not yet exist on the respective network: | ||
|
||
``` | ||
npx hardhat run scripts/deploy.js --network <one_of_the_networks_in_hardhat.config.js> | ||
``` | ||
|
||
The script will output the address of the `Disperse` contract you can add to the table above and use in the `disperse.js` script below. | ||
|
||
## Usage | ||
|
||
Adjust the `disperse.js` script as follows: | ||
|
||
- change the address in line 9 to the address at which the `Disperse` contract is deployed on the respective network, | ||
- change the address in line 10 to the address at which your `ERC20` token is deployed on the respective network, | ||
- change how many accounts to send tokens to per `batch` in line 11 | ||
|
||
Prepare the `input.csv` file. Each line consists of two values separated by a comma: | ||
|
||
- the value before the comma is a hex or bech32 formatted address | ||
- the value after the comma is an integer amount | ||
|
||
Note that the script does not support decimal numbers as amount. | ||
|
||
Transfer the total amount of tokens to be distributed to the account that will be used to execute the script as configured in `hardhat.config.js`. | ||
|
||
Run the script: | ||
|
||
``` | ||
npx hardhat run scripts/disperse.js --network <one_of_the_networks_in_hardhat.config.js> | ||
``` |
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,31 @@ | ||
// SPDX-License-Identifier: MIT | ||
pragma solidity ^0.8.20; | ||
|
||
import "@openzeppelin/contracts/token/ERC20/ERC20.sol"; | ||
import "@openzeppelin/contracts/token/ERC20/IERC20.sol"; | ||
|
||
contract Disperse { | ||
|
||
function disperseEther(address payable[] memory recipients, uint256[] memory values) external payable { | ||
for (uint256 i = 0; i < recipients.length; i++) | ||
recipients[i].transfer(values[i]); | ||
uint256 balance = address(this).balance; | ||
if (balance > 0) | ||
payable(msg.sender).transfer(balance); | ||
} | ||
|
||
function disperseToken(IERC20 token, address[] memory recipients, uint256[] memory values) external { | ||
uint256 total = 0; | ||
for (uint256 i = 0; i < recipients.length; i++) | ||
total += values[i]; | ||
require(token.transferFrom(msg.sender, address(this), total)); | ||
for (uint256 i = 0; i < recipients.length; i++) | ||
require(token.transfer(recipients[i], values[i])); | ||
} | ||
|
||
function disperseTokenSimple(IERC20 token, address[] memory recipients, uint256[] memory values) external { | ||
for (uint256 i = 0; i < recipients.length; i++) | ||
require(token.transferFrom(msg.sender, recipients[i], values[i])); | ||
} | ||
} | ||
|
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,15 @@ | ||
const hre = require("hardhat"); | ||
|
||
async function main() { | ||
const Disperse = await ethers.getContractFactory("Disperse"); | ||
const disperse = await Disperse.deploy(); | ||
await disperse.waitForDeployment(); | ||
console.log(disperse.target); | ||
} | ||
|
||
main() | ||
.then(() => process.exit(0)) | ||
.catch((error) => { | ||
console.error(error); | ||
process.exit(1); | ||
}); |
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,58 @@ | ||
const hre = require("hardhat"); | ||
|
||
const { fromBech32Address, toBech32Address } = require("@zilliqa-js/crypto"); | ||
const { validation } = require("@zilliqa-js/util"); | ||
const { open } = require("node:fs/promises"); | ||
|
||
async function main() { | ||
const disperse = await ethers.getContractAt( | ||
"Disperse", | ||
"0x38048F4B71a87a31d21C86FF373a91d1E401bea5", | ||
); | ||
const token = await ethers.getContractAt( | ||
"ERC20", | ||
"0xf01f7FF8E38759707eE4167f0db48694677D15ad", | ||
); | ||
const batch = 100; | ||
|
||
const decimals = await token.decimals(); | ||
const multiplier = BigInt(10) ** decimals; | ||
|
||
const recipients = []; | ||
const amounts = []; | ||
var total = BigInt(0); | ||
const file = await open("./scripts/input.csv"); | ||
for await (const line of file.readLines()) { | ||
const [address, amountStr] = line.split(","); | ||
const recipient = | ||
address && validation.isBech32(address) | ||
? fromBech32Address(address) | ||
: address; | ||
recipients.push(recipient.toLowerCase()); | ||
const amount = BigInt(amountStr); | ||
amounts.push(amount * multiplier); | ||
total += amount * multiplier; | ||
//console.log(recipient, amount); | ||
} | ||
|
||
txn = await token.approve(disperse, total); | ||
rcpt = await txn.wait(); | ||
|
||
for (start = 0; start < recipients.length; start += batch) { | ||
end = start + batch < recipients.length ? start + batch : recipients.length; | ||
txn = await disperse.disperseToken( | ||
token, | ||
recipients.slice(start, end), | ||
amounts.slice(start, end), | ||
); | ||
rcpt = await txn.wait(); | ||
console.log(txn.hash, start, end); | ||
} | ||
} | ||
|
||
main() | ||
.then(() => process.exit(0)) | ||
.catch((error) => { | ||
console.error(error); | ||
process.exit(1); | ||
}); |
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,2 @@ | ||
zil1zh7ry007t4wul0hdcfwwe06h7emxxnthszxznw,100 | ||
0x15fc323DFE5D5DCfbeEdc25CEcbf57f676634d77,200 |