Skip to content

Commit 0ad12cd

Browse files
Axel ValavaaraAxel Valavaara
authored andcommitted
Added A3A token to serve as fee for prompts
1 parent 4ee37b3 commit 0ad12cd

5 files changed

Lines changed: 136 additions & 12 deletions

File tree

script/DeployOrderContract.s.sol

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -6,18 +6,23 @@ pragma solidity ^0.8.18;
66
import {OrderContract} from "../src/OrderContract.sol";
77
import {Script} from "forge-std/Script.sol";
88
import {HelperConfig} from "./HelperConfig.s.sol";
9-
9+
import {A3AToken} from "../src/A3Atoken.sol";
1010
contract DeployOrderContract is Script {
1111

1212

13-
function run() public returns (OrderContract, HelperConfig) {
13+
function run() public returns (OrderContract, HelperConfig, A3AToken) {
1414
HelperConfig helperConfig = new HelperConfig();
15+
1516
(address pyUSD, address agentController, uint256 deployerKey )= helperConfig.activeNetworkConfig();
17+
1618
vm.startBroadcast(deployerKey);
19+
A3AToken token = new A3AToken();
1720
OrderContract orderContract = new OrderContract(
18-
agentController, pyUSD // Replace with actual pyUSD token address
21+
agentController, pyUSD, address(token) // Replace with actual pyUSD token address
1922
);
23+
token.transferOwnership(address(orderContract));
24+
2025
vm.stopBroadcast();
21-
return (orderContract, helperConfig);
26+
return (orderContract, helperConfig, token);
2227
}
2328
}

src/A3Atoken.sol

Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
// SPDX-License-Identifier: MIT
2+
3+
// Layout of Contract:
4+
// version
5+
// imports
6+
// errors
7+
// interfaces, libraries, contracts
8+
// Type declarations
9+
// State variables
10+
// Events
11+
// Modifiers
12+
// Functions
13+
14+
// Layout of Functions:
15+
// constructor
16+
// receive function (if exists)
17+
// fallback function (if exists)
18+
// external
19+
// public
20+
// internal
21+
// private
22+
// view & pure functions
23+
24+
pragma solidity ^0.8.18;
25+
26+
import {ERC20Burnable, ERC20} from "@openzeppelin/contracts/token/ERC20/extensions/ERC20Burnable.sol";
27+
import {Ownable} from "@openzeppelin/contracts/access/Ownable.sol";
28+
29+
30+
contract A3AToken is ERC20Burnable, Ownable {
31+
/* Erors */
32+
error A3AToken__BurnAmountMustBeMoreThanZero();
33+
error A3AToken__BurnAmountMustBeMoreThanBalance();
34+
error A3AToken__MintToTheZeroAddress();
35+
error A3AToken__MintAmountMustBeMoreThanZero();
36+
37+
constructor() ERC20("Decentralized Stable Coin", "DSC") Ownable(msg.sender) {}
38+
39+
function burn(uint256 _amount) public override onlyOwner {
40+
uint256 balance = balanceOf(msg.sender);
41+
42+
if (_amount <= 0) {
43+
revert A3AToken__BurnAmountMustBeMoreThanZero();
44+
}
45+
if (balance < _amount) {
46+
revert A3AToken__BurnAmountMustBeMoreThanBalance();
47+
}
48+
super.burn(_amount);
49+
}
50+
51+
function mint(address _to, uint256 _amount) external onlyOwner returns (bool) {
52+
if (_to == address(0)) {
53+
revert A3AToken__MintToTheZeroAddress();
54+
}
55+
if (_amount <= 0) {
56+
revert A3AToken__MintAmountMustBeMoreThanZero();
57+
}
58+
_mint(_to, _amount);
59+
return true;
60+
}
61+
}

src/OrderContract.sol

Lines changed: 29 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ pragma solidity ^0.8.18;
2525

2626
import {ERC20} from "@openzeppelin/contracts/token/ERC20/ERC20.sol";
2727
import {ReentrancyGuard} from "@openzeppelin/contracts/utils/ReentrancyGuard.sol";
28+
import {A3AToken} from "./A3Atoken.sol";
2829

2930
contract OrderContract is ReentrancyGuard{
3031
/* errors */
@@ -57,11 +58,13 @@ contract OrderContract is ReentrancyGuard{
5758
OrderStatus status;
5859
}
5960
/* state variables */
60-
uint256 private constant AGENT_FEE = 1 ether; // Fee for agent services. Adjust as needed.
61+
uint256 private constant ADDITIONAL_PRECISION = 1e12; // To handle decimals for tokens with less than 18 decimals
62+
uint256 private constant AGENT_FEE = 1e6; // Fee for agent services. Adjust as needed.
6163
uint256 private constant HOLD_UNTIL = 600; // Time in seconds to hold the order. Adjust as needed.
6264
uint64 private offerID = 0; // Counter for offer IDs
6365
address private immutable agentController; // Address of the agent controller
6466
address private immutable pyUSD; // Address of the pyUSD token contract
67+
address private immutable a3aToken; // Address of the A3A token contract
6568

6669
// will make mappings to struct!!! thus too messy.
6770
//////////////////////////////////////////////
@@ -104,10 +107,11 @@ contract OrderContract is ReentrancyGuard{
104107
_;
105108

106109
}
107-
constructor(address agentControllerAddress, address pyUSDAddress) {
110+
constructor(address agentControllerAddress, address pyUSDAddress, address a3aTokenAddress) {
108111
// Initialization logic if needed
109112
agentController = agentControllerAddress;
110113
pyUSD = pyUSDAddress;
114+
a3aToken = a3aTokenAddress;
111115
}
112116
function proposeOrder(bytes32 promptHash) external returns(uint64 offerId) {
113117

@@ -125,12 +129,23 @@ contract OrderContract is ReentrancyGuard{
125129

126130
// addressToOfferIdToPromptHash[msg.sender][offerID] = promptHash;
127131
// offerIdToUser[offerID] = msg.sender;
132+
128133
emit OrderProposed(offers[offerID].buyer, offerID, offers[offerID].promptHash);
129-
134+
// Burn 10 A3A tokens from uses. This acts as Fee for using the platform.
135+
_burnA3A(10 ether, msg.sender);
130136
return offerID;
131137

132138
}
133139

140+
function _burnA3A(uint256 amount,address A3AFrom) private {
141+
142+
bool success = A3AToken(a3aToken).transferFrom(A3AFrom, address(this), amount);
143+
if (!success) {
144+
revert OrderContract__ERC20TransferFailed();
145+
}
146+
A3AToken(a3aToken).burn(amount);
147+
}
148+
134149

135150

136151
function confirmOrder(uint64 offerId) external nonReentrant onlyUserWithOffer(offerId) {
@@ -203,6 +218,16 @@ contract OrderContract is ReentrancyGuard{
203218
}
204219

205220

221+
function buyA3AToken(uint256 PyUsdAmount) external nonReentrant {
222+
223+
bool success = ERC20(pyUSD).transferFrom(msg.sender, address(this), PyUsdAmount);
224+
if (!success) {
225+
revert OrderContract__ERC20TransferFailed();
226+
}
227+
A3AToken(a3aToken).mint(msg.sender, (PyUsdAmount*ADDITIONAL_PRECISION)*10);
228+
}
229+
230+
206231

207232
/* getter functions */
208233
function getPromptHash(uint64 offerId) external view returns (bytes32) {
@@ -334,5 +359,4 @@ contract OrderContract is ReentrancyGuard{
334359
}
335360

336361

337-
338-
}
362+
}

test/fuzz/InvariantsTest.t.sol

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ contract InvariantsTest is StdInvariant, Test {
1616

1717
function setUp() public {
1818
DeployOrderContract deployer = new DeployOrderContract();
19-
(orderContract, helperConfig) = deployer.run();
19+
(orderContract, helperConfig,) = deployer.run();
2020
targetContract(address(orderContract));
2121
}
2222

test/unit/OrderContractTest.t.sol

Lines changed: 36 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,17 +7,20 @@ import {OrderContract} from "../../src/OrderContract.sol";
77
import {HelperConfig} from "script/HelperConfig.s.sol";
88
import {DeployOrderContract} from "script/DeployOrderContract.s.sol";
99
import {ERC20Mock} from "@openzeppelin/contracts/mocks/token/ERC20Mock.sol";
10+
import {A3AToken} from "../../src/A3Atoken.sol";
1011

1112
contract OrderContractTest is Test {
1213
OrderContract orderContract;
1314
HelperConfig helperConfig;
1415
address pyUSD;
1516
address addressController;
17+
A3AToken a3aToken;
1618

1719
uint256 constant amountForCompute = 10 ether;
1820
bytes32 constant promptHash = keccak256(abi.encodePacked("Test Prompt"));
1921

2022
address public USER = makeAddr("user");
23+
address public USER2 = makeAddr("user2");
2124
address public SELLER = makeAddr("seller");
2225

2326
event OrderProposed(address indexed user, uint64 indexed offerId, bytes32 indexed promptHash);
@@ -26,14 +29,45 @@ contract OrderContractTest is Test {
2629

2730
function setUp() public {
2831
DeployOrderContract deployer = new DeployOrderContract();
29-
(orderContract, helperConfig) = deployer.run();
32+
(orderContract, helperConfig, a3aToken) = deployer.run();
3033
(pyUSD, addressController, ) = helperConfig.activeNetworkConfig();
3134
ERC20Mock(pyUSD).mint(USER, 100 ether);
35+
ERC20Mock(pyUSD).mint(USER2, 100 ether);
36+
37+
// approve the orderContract to spend USERs A3A tokens so no need to approve every time in tests
38+
vm.prank(USER);
39+
a3aToken.approve(address(orderContract), type(uint256).max);
40+
// buy A3A tokens for USER so tests work
41+
vm.startPrank(USER);
42+
ERC20Mock(pyUSD).approve(address(orderContract), 10e6);
43+
orderContract.buyA3AToken(10e6);
44+
vm.stopPrank();
45+
46+
3247

48+
}
49+
//////////////////////////////////////
50+
// Buy A3A tests //
51+
//////////////////////////////////////
52+
function testBuyA3AToken() public {
53+
54+
// Arrange
55+
uint256 amountToBuy = 10e6;
56+
uint256 expectedBalanceOfA3A = 100 ether;
57+
// Act
58+
vm.startPrank(USER2);
59+
ERC20Mock(pyUSD).approve(address(orderContract), amountToBuy);
60+
orderContract.buyA3AToken(amountToBuy);
61+
vm.stopPrank();
62+
// Assert
63+
uint256 userBalanceAfter = a3aToken.balanceOf(USER2);
64+
assertEq(userBalanceAfter, expectedBalanceOfA3A);
65+
3366
}
3467
//////////////////////////////////////
3568
// proposeOrder tests //
3669
//////////////////////////////////////
70+
3771
function testProposeOrderUpdatesVariables() public {
3872

3973

@@ -239,7 +273,7 @@ contract OrderContractTest is Test {
239273

240274
function testGetAgentFee() public view {
241275
// Arrange
242-
uint256 expectedAgentFee = 1 ether; // AGENT_FEE is set to 1 ether in the contract
276+
uint256 expectedAgentFee = 1e6; // AGENT_FEE is set to 1 ether in the contract
243277
// Act
244278
uint256 agentFee = orderContract.getAgentFee();
245279
// Assert

0 commit comments

Comments
 (0)