Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Feature/mint and call #138

Closed
wants to merge 76 commits into from
Closed
Show file tree
Hide file tree
Changes from 2 commits
Commits
Show all changes
76 commits
Select commit Hold shift + click to select a range
cd1dd44
add first demo
malteish Apr 28, 2023
aae4eed
Prettified Code!
malteish Apr 28, 2023
f3ef099
transfer instead of transferFrom, add try catch
malteish May 2, 2023
a4041dc
fix test
malteish May 2, 2023
994b767
Bump decode-uri-component from 0.2.0 to 0.2.2
dependabot[bot] May 8, 2023
477b3dc
Bump cookiejar from 2.1.3 to 2.1.4
dependabot[bot] May 8, 2023
eb193a8
natSpec for Token
malteish May 2, 2023
9be8005
natSpec for ContinuousFundraising
malteish May 2, 2023
2cf81d9
natSpec for PersonalInvite
malteish May 2, 2023
21411f8
more natSpec for PersonalInvite
malteish May 2, 2023
ada3c80
natSpec for PersonalInviteFactory
malteish May 2, 2023
27e060a
natSpec for AllowList
malteish May 3, 2023
36a9662
natSpec for FeeSettings
malteish May 3, 2023
ff48a5d
more natSpec
malteish May 3, 2023
cc31cc5
typo
malteish May 3, 2023
e2350da
refactor ERC20 helper
malteish May 3, 2023
0a81777
test permit for EUROC
malteish May 3, 2023
0e6d24d
Prettified Code!
malteish May 3, 2023
448304a
test USDC
malteish May 3, 2023
8facdd0
Prettified Code!
malteish May 3, 2023
b83f5e3
add permit for DAI
malteish May 3, 2023
636aae8
Prettified Code!
malteish May 3, 2023
945835d
cleanup
malteish May 3, 2023
3b8f8b6
add basic test for VestingWallet
malteish May 5, 2023
ad7d5df
add test of timeLock with PersonalInvite
malteish May 5, 2023
d0860ff
fix test
malteish May 5, 2023
feb1a8b
remove workspace file
malteish May 5, 2023
7832e2a
Bump @openzeppelin/contracts from 4.8.0 to 4.8.3
dependabot[bot] May 8, 2023
bcdc4e8
test AllowList Events
malteish May 8, 2023
6c0938d
test ContinuousFundraising events
malteish May 8, 2023
e11b29e
test FeeSettings events
malteish May 8, 2023
7513e4a
test PersonalInvite event
malteish May 8, 2023
20caf4b
test PersonalInviteFactory events
malteish May 8, 2023
8097aab
test various salts
malteish May 8, 2023
b59b198
test Token events
malteish May 8, 2023
ec3d699
bump prettier solidity version
malteish May 8, 2023
8230597
run prettier
malteish May 8, 2023
0b78b95
change prettier config, reformat all files
malteish May 8, 2023
9987163
Prettified Code!
malteish May 8, 2023
2a57161
replace literal uint256max
malteish May 8, 2023
932c0bd
Prettified Code!
malteish May 9, 2023
a2fdff1
Merge branch 'develop' into feature/mintAndCall
malteish Aug 11, 2023
2599829
improve docs
malteish Aug 11, 2023
04959c4
add calculateBuyAmount function to fundraising
malteish Aug 11, 2023
f63397d
improve test
malteish Aug 11, 2023
048fe2c
add test for stealing funds
malteish Aug 11, 2023
eb04770
docs
malteish Aug 11, 2023
1646efe
limit onTransferReceived to payment token contract
malteish Aug 11, 2023
e0f7638
add ERC1271 to wallet
malteish Aug 14, 2023
7c7c713
prepare test
malteish Aug 14, 2023
3909ce3
fix empty test
malteish Sep 13, 2023
52a23a8
update docs on contract verification
malteish Aug 22, 2023
a40d418
add link
malteish Aug 22, 2023
dd8c827
add upgradeable contracts and token clone factory
malteish Aug 17, 2023
8680618
fix dependencies, add TokenCloneFactory tests
malteish Aug 17, 2023
17d2746
use token params as salt, update tests
malteish Aug 17, 2023
fe34d04
update tests
malteish Aug 17, 2023
924325d
update tests
malteish Aug 17, 2023
e6823e3
update tests
malteish Aug 17, 2023
d136927
fix token tests
malteish Aug 22, 2023
8b537b2
fix tests
malteish Aug 22, 2023
8f073b2
fix tests
malteish Aug 22, 2023
bde009e
fix test
malteish Aug 23, 2023
ff212a8
add salt
malteish Aug 23, 2023
b25c2ad
fix test
malteish Aug 23, 2023
1849765
fix test
malteish Aug 23, 2023
3cf5805
update hardhat
malteish Sep 1, 2023
d5a8c43
add clean command
malteish Sep 1, 2023
0d9f5ea
update openzeppelin
malteish Sep 1, 2023
f8a3e4f
update typescript and node types
malteish Sep 1, 2023
c1ec281
update hardhat packages
malteish Sep 1, 2023
4d74277
update ethers
malteish Sep 1, 2023
aa32fe2
update chai
malteish Sep 1, 2023
5574024
update eslint
malteish Sep 1, 2023
9e99e13
update solhint and typechain
malteish Sep 1, 2023
3d6bba2
update import and test
malteish Sep 13, 2023
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
71 changes: 71 additions & 0 deletions contracts/Wallet.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
// SPDX-License-Identifier: AGPL-3.0-only
pragma solidity 0.8.17;

import "@openzeppelin/contracts/access/Ownable2Step.sol";
import "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol";
import "./interfaces/ERC1363.sol";
import "./ContinuousFundraising.sol";

/**

*/

contract Wallet is Ownable2Step, ERC1363Receiver {
using SafeERC20 for IERC20;
/**
@notice stores the receiving address for each IBAN hash
*/
mapping(bytes32 => address) public receiverAddress;

ContinuousFundraising public fundraising;

event Set(bytes32 indexed ibanHash, address tokenReceiver);

constructor(ContinuousFundraising _fundraising) Ownable2Step() {
fundraising = _fundraising;
}

/**
@notice sets (or updates) the receiving address for an IBAN hash
*/
function set(bytes32 _ibanHash, address _tokenReceiver) external onlyOwner {
receiverAddress[_ibanHash] = _tokenReceiver;
malteish marked this conversation as resolved.
Show resolved Hide resolved
emit Set(_ibanHash, _tokenReceiver);
}

/**
* @notice ERC1363 callback
* @dev to support the mintAndCall standard, this function MUST NOT revert!
*/
function onTransferReceived(
malteish marked this conversation as resolved.
Show resolved Hide resolved
address operator,
address from,
uint256 value,
bytes memory data
) external override returns (bytes4) {
bytes32 ibanHash = abi.decode(data, (bytes32));
if (receiverAddress[ibanHash] == address(0)) {
return bytes4(0xDEADD00D); // ERC1363ReceiverNotRegistered
}
address tokenReceiver = receiverAddress[ibanHash];
// todo: calculate amount
uint256 amount = 100;
// grant allowance to fundraising
IERC20(fundraising.currency()).approve(address(fundraising), amount);
// todo: add try catch https://solidity-by-example.org/try-catch/
fundraising.buy(amount, tokenReceiver);
return 0x600D600D; // ERC1363ReceiverSuccess
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Related to @CJentzsch comment above ERC677 expects a bool to be returned.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

}

/*
@notice withdraws tokens to a given address
*/
function withdraw(
address token,
address to,
uint256 amount
) external onlyOwner {
IERC20(token).safeTransferFrom(address(this), to, amount);
malteish marked this conversation as resolved.
Show resolved Hide resolved
}
}
14 changes: 14 additions & 0 deletions contracts/interfaces/ERC1363.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
// SPDX-License-Identifier: AGPL-3.0-only
pragma solidity 0.8.17;

/**
@notice ERC1363 interface, see https://eips.ethereum.org/EIPS/eip-1363
*/
interface ERC1363Receiver {
function onTransferReceived(
address operator,
address from,
uint256 value,
bytes memory data
) external returns (bytes4);
}
115 changes: 115 additions & 0 deletions test/BuyWithMintAndCall.t.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,115 @@
// SPDX-License-Identifier: AGPL-3.0-only
pragma solidity ^0.8.13;

import "../lib/forge-std/src/Test.sol";
import "../contracts/Token.sol";
import "../contracts/FeeSettings.sol";
import "../contracts/ContinuousFundraising.sol";
import "./resources/MintAndCallToken.sol";
import "./resources/MaliciousPaymentToken.sol";
import "../contracts/Wallet.sol";

contract BuyWithMintAndCall is Test {
ContinuousFundraising raise;
AllowList list;
IFeeSettingsV1 feeSettings;

Token token;
MintAndCallToken paymentToken;
Wallet wallet;

address public constant admin = 0x0109709eCFa91a80626FF3989D68f67f5b1dD120;
address public constant buyer = 0x1109709ecFA91a80626ff3989D68f67F5B1Dd121;
address public constant mintAllower =
0x2109709EcFa91a80626Ff3989d68F67F5B1Dd122;
address public constant minter = 0x3109709ECfA91A80626fF3989D68f67F5B1Dd123;
address public constant owner = 0x6109709EcFA91A80626FF3989d68f67F5b1dd126;
address public constant receiver =
0x7109709eCfa91A80626Ff3989D68f67f5b1dD127;
address public constant paymentTokenProvider =
0x8109709ecfa91a80626fF3989d68f67F5B1dD128;
address public constant trustedForwarder =
0x9109709EcFA91A80626FF3989D68f67F5B1dD129;

uint8 public constant paymentTokenDecimals = 6;
uint256 public constant paymentTokenAmount =
1000 * 10 ** paymentTokenDecimals;

uint256 public constant price = 7 * 10 ** paymentTokenDecimals; // 7 payment tokens per token

uint256 public constant maxAmountOfTokenToBeSold = 20 * 10 ** 18; // 20 token
uint256 public constant maxAmountPerBuyer = maxAmountOfTokenToBeSold / 2; // 10 token
uint256 public constant minAmountPerBuyer = 1;

function setUp() public {
list = new AllowList();
Fees memory fees = Fees(100, 100, 100, 100);
feeSettings = new FeeSettings(fees, admin);

token = new Token(
trustedForwarder,
feeSettings,
admin,
list,
0x0,
"TESTTOKEN",
"TEST"
);

// set up currency
vm.startPrank(paymentTokenProvider);
paymentToken = new MintAndCallToken(paymentTokenDecimals);
vm.stopPrank();

vm.prank(owner);
raise = new ContinuousFundraising(
trustedForwarder,
payable(receiver),
minAmountPerBuyer,
maxAmountPerBuyer,
price,
maxAmountOfTokenToBeSold,
paymentToken,
token
);

// allow raise contract to mint
bytes32 roleMintAllower = token.MINTALLOWER_ROLE();

vm.prank(admin);
token.grantRole(roleMintAllower, mintAllower);
vm.prank(mintAllower);
token.increaseMintingAllowance(
address(raise),
maxAmountOfTokenToBeSold
);

// give raise contract allowance
vm.prank(buyer);
paymentToken.approve(address(raise), paymentTokenAmount);

// create wallet
vm.prank(owner);
wallet = new Wallet(raise);
}

function testBuyHappyCase() public {
bytes32 buyersIbanHash = keccak256(abi.encodePacked("DE1234567890"));
// add buyers address to wallet
vm.prank(owner);
wallet.set(buyersIbanHash, buyer);

uint currencyMintAmount = 1e20;

// make sure buyer has no tokens before
assertTrue(token.balanceOf(buyer) == 0);

// mint currency
bytes memory data = abi.encode(buyersIbanHash, 0xDEADBEEF);
vm.prank(paymentTokenProvider);
paymentToken.mintAndCall(address(wallet), currencyMintAmount, data);

// make sure buyer has tokens after
assertTrue(token.balanceOf(buyer) > 0);
}
}
40 changes: 40 additions & 0 deletions test/resources/MintAndCallToken.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
// SPDX-License-Identifier: AGPL-3.0-only
pragma solidity ^0.8.13;

import "../../node_modules/@openzeppelin/contracts/token/ERC20/ERC20.sol";
import "../../node_modules/@openzeppelin/contracts/access/Ownable.sol";
import "../../contracts/interfaces/ERC1363.sol";

/*
fake currency to test the mint and call feature
*/
contract MintAndCallToken is ERC20, Ownable {
uint8 decimalPlaces;

constructor(uint8 _decimals) ERC20("MintAndCallToken", "MACT") Ownable() {
decimalPlaces = _decimals;
}

/// @dev price definition and deal() function rely on proper handling of decimalPlaces. Therefore we need to test if decimalPlaces other than 18 work fine, too.
function decimals() public view override returns (uint8) {
return decimalPlaces;
}

function mint(address _to, uint256 _amount) external onlyOwner {
_mint(_to, _amount);
}

function mintAndCall(
address _to,
uint256 _amount,
bytes memory _data
) external onlyOwner {
_mint(_to, _amount);
ERC1363Receiver(_to).onTransferReceived(
msg.sender,
address(0),
_amount,
_data
);
}
}