Skip to content

Commit

Permalink
first draft
Browse files Browse the repository at this point in the history
  • Loading branch information
moodysalem committed Sep 2, 2020
0 parents commit dbc0b32
Show file tree
Hide file tree
Showing 14 changed files with 7,977 additions and 0 deletions.
1 change: 1 addition & 0 deletions .gitattributes
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
*.sol linguist-language=Solidity
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
node_modules/
build/
6 changes: 6 additions & 0 deletions .mocharc.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
{
"extension": ["ts"],
"spec": "./test/**/*.spec.ts",
"require": "ts-node/register",
"timeout": 12000
}
5 changes: 5 additions & 0 deletions .prettierrc
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
{
"semi": false,
"singleQuote": true,
"printWidth": 120
}
1 change: 1 addition & 0 deletions .yarnrc
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
ignore-scripts true
24 changes: 24 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
# @uniswap/token-wrapper-factory

![Tests](https://github.com/Uniswap/token-wrapper-factory/workflows/Tests/badge.svg)
![Lint](https://github.com/Uniswap/token-wrapper-factory/workflows/Lint/badge.svg)

This repository contains an ERC20 token wrapper factory.

Tokens produced by this factory represent the underlying.

# Local Development

The following assumes the use of `node@>=10`.

## Install Dependencies

`yarn`

## Compile Contracts

`yarn compile`

## Run Tests

`yarn test`
36 changes: 36 additions & 0 deletions contracts/AirdropTokenDistributor.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
// SPDX-License-Identifier: UNLICENSED
pragma solidity =0.6.11;

import "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import "@openzeppelin/contracts/token/ERC20/SafeERC20.sol";
import "@openzeppelin/contracts/cryptography/MerkleProof.sol";
import "./interfaces/IAirdropTokenDistributor.sol";

contract AirdropTokenDistributor is IAirdropTokenDistributor {
using SafeMath for uint256;
using SafeERC20 for IERC20;

address public immutable override token;
bytes32 public immutable override merkleRoot;
mapping(address => mapping(uint => bool)) public override isClaimed;

constructor(address token_, bytes32 merkleRoot_) public {
token = token_;
merkleRoot = merkleRoot_;
}

function claim(address account, uint amount, bytes32[] calldata merkleProof) external override {
require(!isClaimed[account][amount], 'AirdropTokenDistributor: Drop already claimed.');

// Verify the merkle proof.
bytes32 node = keccak256(abi.encodePacked(account, amount));

require(MerkleProof.verify(merkleProof, merkleRoot, node), 'AirdropTokenDistributor: Invalid proof.');

// Mark it claimed and send the token.
isClaimed[account][amount] = true;
IERC20(token).safeTransfer(account, amount);

emit Claimed(account, amount);
}
}
16 changes: 16 additions & 0 deletions contracts/interfaces/IAirdropTokenDistributor.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
// SPDX-License-Identifier: UNLICENSED
pragma solidity >=0.5.0;

// Allows anyone to claim a token if they exist in a merkle root.
interface IAirdropTokenDistributor {
// Returns the address of the token distributed by this contract.
function token() external view returns (address);
// Returns the merkle root of the merkle tree containing account balances available to claim.
function merkleRoot() external view returns (bytes32);
// Returns true if the account has successfully claimed the amount.
function isClaimed(address account, uint amount) external view returns (bool);
// Claim the given amount of the token to the given address. Reverts if the inputs are invalid.
function claim(address account, uint amount, bytes32[] calldata merkleProof) external;

event Claimed(address account, uint amount);
}
21 changes: 21 additions & 0 deletions contracts/test/TestERC20.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
// SPDX-License-Identifier: UNLICENSED
pragma solidity =0.6.11;

import '@openzeppelin/contracts/token/ERC20/ERC20.sol';

contract TestERC20 is ERC20 {
constructor (string memory name_, string memory symbol_, uint amountToMint) ERC20(name_, symbol_) public {
setBalance(msg.sender, amountToMint);
}

// sets the balance of the address
// this mints/burns the amount depending on the current balance
function setBalance(address to, uint amount) public {
uint old = balanceOf(to);
if (old < amount) {
_mint(to, amount - old);
} else if (old > amount) {
_burn(to, old - amount);
}
}
}
45 changes: 45 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
{
"name": "@uniswap/token-wrapper-factory",
"description": "🤨 ERC20 wrapper factory that removes degenerate behavior",
"version": "1.0.0",
"homepage": "https://uniswap.org",
"keywords": [
"uniswap",
"erc20",
"degenerate"
],
"repository": {
"type": "git",
"url": "https://github.com/Uniswap/token-wrapper-factory"
},
"files": [
"contracts",
"build"
],
"engines": {
"node": ">=10"
},
"dependencies": {
"@openzeppelin/contracts": "3.1.0",
"@uniswap/lib": "1.1.4"
},
"devDependencies": {
"@types/chai": "^4.2.6",
"@types/mocha": "^5.2.7",
"chai": "^4.2.0",
"ethereum-waffle": "^3.0.0",
"mocha": "^6.2.2",
"prettier": "^2.0.5",
"rimraf": "^3.0.0",
"solc": "0.6.11",
"ts-node": "^8.5.4",
"typescript": "^3.7.3"
},
"scripts": {
"precompile": "rimraf ./build/",
"compile": "waffle",
"pretest": "yarn compile",
"test": "mocha",
"prepublishOnly": "yarn test"
}
}
37 changes: 37 additions & 0 deletions test/AirdropTokenDistributor.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
import chai, { expect } from 'chai'
import { solidity, MockProvider, deployContract } from 'ethereum-waffle'
import { Contract, BigNumber } from 'ethers'

import Airdrop from '../build/AirdropTokenDistributor.json'
import TestERC20 from '../build/TestERC20.json'

chai.use(solidity)

const overrides = {
gasLimit: 9999999,
}

const ZERO_BYTES32 = '0x0000000000000000000000000000000000000000000000000000000000000000'

describe('AirdropTokenDistributor', () => {
const provider = new MockProvider({
ganacheOptions: {
hardfork: 'istanbul',
mnemonic: 'horn horn horn horn horn horn horn horn horn horn horn horn',
gasLimit: 9999999,
},
})
const [wallet] = provider.getWallets()

let token: Contract
beforeEach('deploy token', async () => {
token = await deployContract(wallet, TestERC20, ['Token', 'TKN', 0], overrides)
})

describe('#token', () => {
it('returns the token address', async () => {
const airdrop = await deployContract(wallet, Airdrop, [token.address, ZERO_BYTES32], overrides)
expect(await airdrop.token()).to.eq(token.address)
})
})
})
9 changes: 9 additions & 0 deletions tsconfig.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
{
"compilerOptions": {
"target": "es5",
"module": "commonjs",
"strict": true,
"esModuleInterop": true,
"resolveJsonModule": true
}
}
25 changes: 25 additions & 0 deletions waffle.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
{
"compilerType": "solcjs",
"compilerVersion": "./node_modules/solc",
"outputType": "all",
"compilerOptions": {
"outputSelection": {
"*": {
"*": [
"evm.bytecode.object",
"evm.deployedBytecode.object",
"abi",
"evm.bytecode.sourceMap",
"evm.deployedBytecode.sourceMap",
"metadata"
],
"": ["ast"]
}
},
"evmVersion": "istanbul",
"optimizer": {
"enabled": true,
"runs": 200
}
}
}
Loading

0 comments on commit dbc0b32

Please sign in to comment.