Skip to content

Commit

Permalink
Friendly Agent
Browse files Browse the repository at this point in the history
  • Loading branch information
haiyanghe committed Sep 16, 2023
1 parent e3af9ba commit 97af5bb
Show file tree
Hide file tree
Showing 16 changed files with 596 additions and 133 deletions.
3 changes: 3 additions & 0 deletions .env.example
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
BASE_RPC=
OWNER_PRIVATE_KEY=
ETHERSCAN_API_KEY=
45 changes: 18 additions & 27 deletions .github/workflows/test.yml
Original file line number Diff line number Diff line change
@@ -1,34 +1,25 @@
name: test
name: CI

on: workflow_dispatch

env:
FOUNDRY_PROFILE: ci
on:
push:
branches:
- main
pull_request:
branches:
- main

jobs:
check:
strategy:
fail-fast: true

name: Foundry project
build-and-test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
with:
submodules: recursive

- name: Install Foundry
uses: foundry-rs/foundry-toolchain@v1
with:
version: nightly
steps:
- name: Checkout repository
uses: actions/checkout@v2

- name: Run Forge build
run: |
forge --version
forge build --sizes
id: build
- name: Install Foundry
uses: foundry-rs/foundry-toolchain@v1
with:
version: nightly

- name: Run Forge tests
run: |
forge test -vvv
id: test
- name: Run make test
run: make test
6 changes: 6 additions & 0 deletions .gitmodules
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
[submodule "lib/forge-std"]
path = lib/forge-std
url = https://github.com/foundry-rs/forge-std
[submodule "lib/openzeppelin-contracts"]
path = lib/openzeppelin-contracts
url = https://github.com/OpenZeppelin/openzeppelin-contracts
23 changes: 23 additions & 0 deletions Makefile
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
-include .env

.PHONY: install
install:
@foundryup
@forge install OpenZeppelin/openzeppelin-contracts --no-commit

.PHONY: test
test:
@forge test -vvvv

.PHONY: deploy
deploy:
@echo "Deploying to Base Mainnet"
@forge script script/Deploy.s.sol --rpc-url $(BASE_RPC) --private-key $(OWNER_PRIVATE_KEY) --broadcast --verify --etherscan-api-key $(ETHERSCAN_API_KEY) -vvvv

.PHONY: build
build:
@forge build

.PHONY: format
format:
@forge fmt
67 changes: 11 additions & 56 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,66 +1,21 @@
## Foundry
## Friendly Agent

**Foundry is a blazing fast, portable and modular toolkit for Ethereum application development written in Rust.**
Helps to make limit orders within a single transaction on Friendtech. Some additional helpers added to withdraw ETH/Arbitrary ERC20s.

Foundry consists of:
Create `.env` file from `.env.example` and fill in the details.

- **Forge**: Ethereum testing framework (like Truffle, Hardhat and DappTools).
- **Cast**: Swiss army knife for interacting with EVM smart contracts, sending transactions and getting chain data.
- **Anvil**: Local Ethereum node, akin to Ganache, Hardhat Network.
- **Chisel**: Fast, utilitarian, and verbose solidity REPL.
## Install

## Documentation
`make install`

https://book.getfoundry.sh/
## Tests

## Usage
`make test`

### Build
## Fork Tests

```shell
$ forge build
```
`make test-fork`

### Test
## Deploy

```shell
$ forge test
```

### Format

```shell
$ forge fmt
```

### Gas Snapshots

```shell
$ forge snapshot
```

### Anvil

```shell
$ anvil
```

### Deploy

```shell
$ forge script script/Counter.s.sol:CounterScript --rpc-url <your_rpc_url> --private-key <your_private_key>
```

### Cast

```shell
$ cast <subcommand>
```

### Help

```shell
$ forge --help
$ anvil --help
$ cast --help
```
`make deploy`
2 changes: 2 additions & 0 deletions foundry.toml
Original file line number Diff line number Diff line change
Expand Up @@ -2,5 +2,7 @@
src = "src"
out = "out"
libs = ["lib"]
remappings = ["@openzeppelin-contracts=lib/openzeppelin-contracts/contracts"]


# See more config options https://github.com/foundry-rs/foundry/blob/master/crates/config/README.md#all-options
1 change: 1 addition & 0 deletions lib/forge-std
Submodule forge-std added at 1d9650
1 change: 1 addition & 0 deletions lib/openzeppelin-contracts
Submodule openzeppelin-contracts added at fd81a9
12 changes: 0 additions & 12 deletions script/Counter.s.sol

This file was deleted.

20 changes: 20 additions & 0 deletions script/DeployFriendlyAgent.s.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
// SPDX-License-Identifier: MIT

pragma solidity ^0.8.18;

import {Script} from "forge-std/Script.sol";
import {FriendlyAgent} from "../src/FriendlyAgent.sol";
import {HelperConfig} from "./HelperConfig.s.sol";

contract DeployFriendlyAgent is Script {
function run() external returns (FriendlyAgent) {
uint256 ownerPrivateKey = vm.envUint("OWNER_PRIVATE_KEY");
HelperConfig helperConfig = new HelperConfig();

vm.startBroadcast(ownerPrivateKey);
FriendlyAgent friendlyAgent = new FriendlyAgent(helperConfig.friendsTechAddress());
vm.stopBroadcast();

return friendlyAgent;
}
}
39 changes: 39 additions & 0 deletions script/HelperConfig.s.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
// SPDX-License-Identifier: MIT

pragma solidity ^0.8.18;

import {Script} from "forge-std/Script.sol";
import {FriendtechSharesV1} from "../src/FriendtechSharesV1.sol";

contract HelperConfig is Script {
address public friendsTechAddress;

constructor() {
if (block.chainid == 8453) {
friendsTechAddress = getBaseMainnet();
} else {
friendsTechAddress = getOrCreateAnvilConfig();
}
}

function getOrCreateAnvilConfig() public returns (address) {
if (friendsTechAddress != address(0)) {
return friendsTechAddress;
}

vm.startBroadcast();
FriendtechSharesV1 friendTechSharesV1 = new FriendtechSharesV1();
friendTechSharesV1.setSubjectFeePercent(0);
friendTechSharesV1.setProtocolFeePercent(0);

// Set to burn address for tests
friendTechSharesV1.setFeeDestination(0x0000000000000000000000000000000000000000);
vm.stopBroadcast();

return address(friendTechSharesV1);
}

function getBaseMainnet() public pure returns (address) {
return 0xCF205808Ed36593aa40a44F10c7f7C2F67d4A4d4;
}
}
14 changes: 0 additions & 14 deletions src/Counter.sol

This file was deleted.

81 changes: 81 additions & 0 deletions src/FriendlyAgent.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
// SPDX-License-Identifier: MIT

pragma solidity ^0.8.18;

interface IFriendtechSharesV1 {
function getBuyPriceAfterFee(address sharesSubject, uint256 amount) external view returns (uint256);
function getSellPriceAfterFee(address sharesSubject, uint256 amount) external view returns (uint256);
function buyShares(address sharesSubject, uint256 amount) external payable;
function sellShares(address sharesSubject, uint256 amount) external payable;
}

interface IERC20 {
function transfer(address recipient, uint256 amount) external returns (bool);
function balanceOf(address account) external view returns (uint256);
}

contract FriendlyAgent {
error FriendlyAgent__NotOwner();
error FriendlyAgent__AmountGreaterThanHoldings();
error FriendlyAgent__OverMaxLimit();
error FriendlyAgent__UnderMinLimit();

IFriendtechSharesV1 public immutable i_friendtechShares;
address public immutable i_owner;
mapping(address => uint256) private s_holdings;

constructor(address _friendtechSharesAddress) {
i_friendtechShares = IFriendtechSharesV1(_friendtechSharesAddress);
i_owner = msg.sender;
}

modifier onlyOwner() {
if (msg.sender != i_owner) {
revert FriendlyAgent__NotOwner();
}
_;
}

function buyShares(uint256 maxPrice, uint256 amount, address sharesSubject) public payable onlyOwner {
uint256 priceAfterFee = i_friendtechShares.getBuyPriceAfterFee(sharesSubject, amount);
if (priceAfterFee > maxPrice) {
revert FriendlyAgent__OverMaxLimit();
}

s_holdings[sharesSubject] += amount;
i_friendtechShares.buyShares{value: priceAfterFee}(sharesSubject, amount);
}

function sellShares(uint256 minPrice, uint256 amount, address sharesSubject) public onlyOwner {
if (s_holdings[sharesSubject] < amount) {
revert FriendlyAgent__AmountGreaterThanHoldings();
}
uint256 priceAfterFee = i_friendtechShares.getSellPriceAfterFee(sharesSubject, amount);
if (priceAfterFee < minPrice) {
revert FriendlyAgent__UnderMinLimit();
}

s_holdings[sharesSubject] -= amount;
i_friendtechShares.sellShares{value: 0}(sharesSubject, amount);
}

function withdraw() public onlyOwner {
(bool callSuccess,) = payable(i_owner).call{value: address(this).balance}("");
require(callSuccess, "Call failed");
}

function withdrawToken(address tokenAddress) public onlyOwner {
IERC20 token = IERC20(tokenAddress);
uint256 balance = token.balanceOf(address(this));
require(balance > 0, "No tokens to withdraw");
require(token.transfer(i_owner, balance), "Token transfer failed");
}

function getHoldings(address sharesSubject) external view returns (uint256) {
return s_holdings[sharesSubject];
}

fallback() external payable {}

receive() external payable {}
}
Loading

0 comments on commit 97af5bb

Please sign in to comment.