Skip to content

Commit

Permalink
feat: Fly ERC20
Browse files Browse the repository at this point in the history
  • Loading branch information
veeso committed Jan 20, 2024
1 parent fd5b4b4 commit 9fd9d3d
Show file tree
Hide file tree
Showing 7 changed files with 207 additions and 61 deletions.
25 changes: 25 additions & 0 deletions .github/workflows/ethereum.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
name: ethereum
on: [push, pull_request]

jobs:
tests:
runs-on: ubuntu-latest

steps:
- uses: actions/checkout@v3
- name: Install packages
uses: actions/setup-node@v3
with:
node-version: "20.x"
- run: yarn --ignore-scripts
shell: bash
- name: Create local node and deploy
run: |
cp .env.github .env &&
yarn hardhat node &
sleep 2 &&
yarn deploy:localhost
working-directory: ./ethereum/fly
- name: tests
run: yarn hardhat test
working-directory: ./ethereum/fly
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
# Deferred

![CI state](https://github.com/veeso-dev/deferred/workflows/build-test/badge.svg)
![Ethereum](https://github.com/veeso-dev/deferred/workflows/ethereum/badge.svg)
[![Conventional Commits](https://img.shields.io/badge/Conventional%20Commits-1.0.0-%23FE5196?logo=conventionalcommits&logoColor=white)](https://conventionalcommits.org)

<img src="./docs/images/logo.png" alt="deferred logo" width="128" />
Expand Down
1 change: 1 addition & 0 deletions ethereum/fly/.env.github
Original file line number Diff line number Diff line change
Expand Up @@ -4,3 +4,4 @@ PROD_PRIVATE_KEY="00000000000000000000000000000000000000000000000000000000000000
DEV_PRIVATE_KEY="0000000000000000000000000000000000000000000000000000000000000000"
LOCAL_PRIVATE_KEY="ac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80"
OWNER_ADDRESS="0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266"
FLY_CANISTER_ETH_ADDRESS="0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266"
23 changes: 0 additions & 23 deletions ethereum/fly/.github/workflows/build-test.yml

This file was deleted.

84 changes: 76 additions & 8 deletions ethereum/fly/contracts/Fly.sol
Original file line number Diff line number Diff line change
Expand Up @@ -8,20 +8,88 @@ import {Ownable} from "@openzeppelin/contracts/access/Ownable.sol";
// import "hardhat/console.sol";

contract Fly is ERC20, Ownable {
address private immutable fly_canister_address;
uint8 private _decimals;
uint256 public swapFee;

event FlySwapped(
address indexed _from,
bytes32 indexed _principal,
uint256 _amount
);

constructor(
string memory name,
string memory symbol,
address initialOwner,
uint256 initialSupply,
uint8 decs
) ERC20(name, symbol) Ownable(initialOwner) {
_mint(initialOwner, initialSupply);
_decimals = decs;
address _initialOwner,
address _fly_canister_address,
uint256 _swapFee
) ERC20("Fly", "FLY") Ownable(_initialOwner) {
_decimals = 12;
fly_canister_address = _fly_canister_address;
swapFee = _swapFee;
}

modifier onlyFlyCanister() {
require(
msg.sender == fly_canister_address,
"Fly: caller is not the fly canister"
);
_;
}

function decimals() public view virtual override returns (uint8) {
return _decimals;
}

/**
* @dev Returns the address of the fly canister.
* @return The address of the fly canister.
*/
function getFlyCanisterAddress() public view returns (address) {
return fly_canister_address;
}

/**
* @dev Sets the swap fee.
* @param _swapFee The new swap fee.
*/
function setSwapFee(uint256 _swapFee) public onlyOwner {
swapFee = _swapFee;
}

/**
* @dev Swaps the Fly tokens from Ethereum blockchain to IC from the caller to the recipient principal for the provided amount.
* @param _recipient The principal to receive the tokens.
* @param _amount The amount of tokens to swap.
*/
function swap(bytes32 _recipient, uint256 _amount) public payable {
// check if the caller has enough tokens to swap
require(
balanceOf(msg.sender) >= _amount,
"Fly: caller does not have enough tokens to swap"
);
// check if the caller has enough ether to pay the fee
require(
msg.value >= swapFee,
"Fly: caller does not have enough ether to pay the fee"
);
// burn the tokens from the caller
_burn(msg.sender, _amount);
// emit swap event
emit FlySwapped(msg.sender, _recipient, _amount);
// pay fee
payable(fly_canister_address).transfer(msg.value);
}

/**
* @dev Mints the provided amount of tokens to the recipient (after a swap on the fly canister).
* @param _recipient the address that will receive the ETH Fly tokens.
* @param _amount the amount of tokens to mint.
*/
function transcribeSwap(
address _recipient,
uint256 _amount
) public onlyFlyCanister {
// mint the tokens to the recipient
_mint(_recipient, _amount);
}
}
10 changes: 4 additions & 6 deletions ethereum/fly/scripts/deploy.ts
Original file line number Diff line number Diff line change
@@ -1,17 +1,15 @@
import { ethers } from "hardhat";
require("dotenv").config();

const { OWNER_ADDRESS } = process.env;
const { OWNER_ADDRESS, FLY_CANISTER_ETH_ADDRESS } = process.env;

async function main() {
// deploy contract
const Contract = await ethers.getContractFactory("MyToken");
const Contract = await ethers.getContractFactory("Fly");
const contract = await Contract.deploy(
"Fly",
"FLY",
OWNER_ADDRESS!,
"8880101010000000000",
12
FLY_CANISTER_ETH_ADDRESS!,
761_035
);
await contract.waitForDeployment();
const address = await contract.getAddress();
Expand Down
124 changes: 100 additions & 24 deletions ethereum/fly/test/Fly.ts
Original file line number Diff line number Diff line change
@@ -1,52 +1,128 @@
import { expect } from "chai";
import { ethers } from "hardhat";
import { MyToken } from "../typechain-types";
import { Fly } from "../typechain-types";
import { SignerWithAddress } from "@nomiclabs/hardhat-ethers/signers";

const INITIAL_SUPPLY = "8880101010000000000"; // 8 milions
// const INITIAL_SUPPLY = "8880101010000000000"; // 8 milions
const NAME = "Fly";
const SYMBOL = "FLY";
const DECIMALS = 12;
const INITIAL_FEE = 100;
const DUMMY_PRINCIPAL = new Uint8Array([
64, 123, 39, 130, 111, 49, 3, 65, 143, 8, 40, 152, 37, 163, 102, 10, 226, 6,
132, 148, 181, 23, 75, 76, 77, 109, 126, 107, 2, 14, 0, 10,
]);

describe("Fly", () => {
interface Contract {
token: MyToken;
token: Fly;
owner: SignerWithAddress;
otherAccount: SignerWithAddress;
flyCanister: SignerWithAddress;
}

let deploy: Contract;
let _name = "Fly";
let _symbol = "FLY";

beforeEach(async () => {
const [owner, otherAccount] = await ethers.getSigners();
const contract = await ethers.deployContract(_name, [
_name,
_symbol,
const signer = await ethers.provider.getSigner(owner.address);

const Contract = await ethers.getContractFactory("Fly");
const contract = await Contract.deploy(
owner.address,
owner.address,
INITIAL_SUPPLY,
DECIMALS,
]);
INITIAL_FEE
);
await contract.waitForDeployment();

const address = await contract.getAddress();
const token = new ethers.Contract(address, Contract.interface, signer);

deploy = {
token: contract as unknown as MyToken,
token: token as unknown as Fly,
owner,
otherAccount,
flyCanister: otherAccount,
};
});

it("Should has the correct name and symbol ", async () => {
const { token, owner } = deploy;
expect(await token.name()).to.equal(_name);
expect(await token.symbol()).to.equal(_symbol);
expect(await token.name()).to.equal(NAME);
expect(await token.symbol()).to.equal(SYMBOL);
expect(await token.decimals()).to.equal(DECIMALS);
expect(await token.swapFee()).to.equal(INITIAL_FEE);
// check balance
expect(await token.balanceOf(owner.address)).to.equal(INITIAL_SUPPLY);
expect(await token.balanceOf(owner.address)).to.equal(0);
});

it("Should transcribe swap", async () => {
const { token, owner, flyCanister } = deploy;
await token.transcribeSwap(owner.address, 100);
expect(await token.balanceOf(owner.address)).to.equal(100);
});

it("Should swap 100 tokens", async () => {
const { token, owner, flyCanister } = deploy;
await token.transcribeSwap(owner.address, 100);
const fee = await token.swapFee();

const initialBalance = await ethers.provider.getBalance(owner.address);

// swap and check event is emitted
await expect(
token.swap(DUMMY_PRINCIPAL, 75, {
value: fee,
})
)
.to.emit(token, "FlySwapped")
.withArgs(owner.address, DUMMY_PRINCIPAL, 75);

expect(await token.balanceOf(owner.address)).to.equal(25);

// check owner has paid FEE ethers
const finalBalance =
initialBalance - (await ethers.provider.getBalance(owner.address));

expect(finalBalance).to.greaterThan(fee);
});

it("should fail swap if fee is not paid", async () => {
const { token, owner, flyCanister } = deploy;
await token.transcribeSwap(owner.address, 100);

await expect(
token.swap(DUMMY_PRINCIPAL, 75, {
value: 10,
})
).to.be.revertedWith(
"Fly: caller does not have enough ether to pay the fee"
);
});

it("should fail swap if has not enough tokens", async () => {
const { token, owner, flyCanister } = deploy;
await token.transcribeSwap(owner.address, 100);
const fee = await token.swapFee();

await expect(
token.swap(DUMMY_PRINCIPAL, 101, {
value: fee,
})
).to.be.revertedWith("Fly: caller does not have enough tokens to swap");
});

it("Should transfer 500 tokens", async () => {
const { otherAccount, token } = deploy;
await token.transfer(otherAccount.address, 500);
expect(await token.balanceOf(otherAccount.address)).to.equal(500);
const { flyCanister, owner, token } = deploy;
await token.transcribeSwap(owner.address, 1_000);

await token.transfer(flyCanister.address, 250);
expect(await token.balanceOf(flyCanister.address)).to.equal(250);
expect(await token.balanceOf(owner.address)).to.equal(750);
});

it("Should update swap fee", async () => {
const { token } = deploy;
await token.setSwapFee(200);
expect(await token.swapFee()).to.equal(200);
});

it("should renounce ownership", async () => {
Expand All @@ -57,9 +133,9 @@ describe("Fly", () => {
);
});

it("should transfer ownership of the NFT", async () => {
const { otherAccount, owner: originalOwner, token } = deploy;
await token.transferOwnership(otherAccount.address);
expect(await token.owner()).to.equal(otherAccount.address);
it("should transfer ownership of the contract", async () => {
const { flyCanister, owner: originalOwner, token } = deploy;
await token.transferOwnership(flyCanister.address);
expect(await token.owner()).to.equal(flyCanister.address);
});
});

0 comments on commit 9fd9d3d

Please sign in to comment.