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

AssetManager Contract #1

Merged
merged 53 commits into from
Aug 10, 2022
Merged
Show file tree
Hide file tree
Changes from 7 commits
Commits
Show all changes
53 commits
Select commit Hold shift + click to select a range
96fc994
Initial draft of AssetManager Contract
johnwhitton Jul 28, 2022
96775c0
Make withdraw only able to be called by Owner
johnwhitton Jul 28, 2022
f5f2b29
Initial draft of transfer functionality
johnwhitton Jul 30, 2022
a3a232c
Remove extraneous folders
johnwhitton Jul 31, 2022
6dbf316
Index events, add errors and reverts, add operator role
johnwhitton Jul 31, 2022
7829e02
Working Transfer function, with events and tested for ERC20, ERC721 a…
johnwhitton Jul 31, 2022
206fd47
Testing cater for gas costs and check events emitted
johnwhitton Aug 1, 2022
f02de7f
Add user limits per destination address using two-level mapping
johnwhitton Aug 2, 2022
31de63c
Update AssetManager to be upgradeable, use access control(multiple op…
johnwhitton Aug 3, 2022
89caeb1
Add ability for admins to change globalUserAuthLimit and check limit …
johnwhitton Aug 4, 2022
f731def
Add in granular positive tests for deposit, withdraw, approve and tra…
johnwhitton Aug 4, 2022
d13252b
Addd global deposit limit
johnwhitton Aug 4, 2022
b19be50
Document AssetManager.sol
johnwhitton Aug 5, 2022
be2007d
Fix minor documentation formatting issues
johnwhitton Aug 5, 2022
06f151d
Add in capability to get operators and operator count by using Access…
johnwhitton Aug 5, 2022
284ec06
fix some typos
polymorpher Aug 7, 2022
918ddca
Merge branch 'main' into jw-contract
polymorpher Aug 7, 2022
88d8ec1
fix relayer description
polymorpher Aug 7, 2022
6c44982
plural add or remove operators
polymorpher Aug 7, 2022
220f637
improve initialize
polymorpher Aug 7, 2022
d419414
fix typos, formats, allow allowance to be called any time
polymorpher Aug 7, 2022
1b9ff86
fix bug re: allowance change after send
polymorpher Aug 7, 2022
67f54cd
fmt
polymorpher Aug 7, 2022
59c7475
fmt
polymorpher Aug 7, 2022
bc2c9e8
rm prettier; refine eslint rules
polymorpher Aug 7, 2022
71ba069
fmt
polymorpher Aug 7, 2022
7198fb5
ignore docs
polymorpher Aug 7, 2022
5af1139
rm docs
polymorpher Aug 7, 2022
636e4c6
fmt
polymorpher Aug 7, 2022
7ab226f
ignore openzeppellin
polymorpher Aug 7, 2022
4893499
ditto
polymorpher Aug 7, 2022
f1fcd34
fix typos and events in tests
polymorpher Aug 7, 2022
c0fc296
fix empty env json parse exception
polymorpher Aug 7, 2022
019fa66
rm duplicate env url
polymorpher Aug 7, 2022
e8791b7
fix bad doc parameters in .sol
polymorpher Aug 7, 2022
1fa401d
minor typo fix
polymorpher Aug 7, 2022
aa0289b
Fix tests on native token transfer to check recipient allowance is re…
johnwhitton Aug 7, 2022
dc73743
Display operators when deploying AssetManager.sol
johnwhitton Aug 7, 2022
722a874
Clean up unused environement variables and create config.test object
johnwhitton Aug 7, 2022
fc603ab
Add in hardhat-contract-sizer and change optimizer.runs to 200
johnwhitton Aug 7, 2022
73ddfd9
Testing modularization, 100 percent coverage, update configuration an…
johnwhitton Aug 8, 2022
960998d
fix typos
polymorpher Aug 9, 2022
a4c15d7
cleanup imports
polymorpher Aug 9, 2022
51ad980
fix minor typos
polymorpher Aug 10, 2022
c9d85ab
comment out suppressed test code and fix typo
polymorpher Aug 10, 2022
5ea1802
clean up "this"
polymorpher Aug 10, 2022
96c8bc1
cleanup imports and move config
polymorpher Aug 10, 2022
21ba43f
eliminates require
polymorpher Aug 10, 2022
26cdf9c
cleanup env
polymorpher Aug 10, 2022
a2d8980
fix compile
polymorpher Aug 10, 2022
539de19
readme
polymorpher Aug 10, 2022
ab6981e
rename stuff
polymorpher Aug 10, 2022
e405015
improve test readme
polymorpher Aug 10, 2022
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
3 changes: 3 additions & 0 deletions chaincode/.env.example
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
ETHERSCAN_API_KEY=ABC123ABC123ABC123ABC123ABC123ABC1
Copy link
Owner

Choose a reason for hiding this comment

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

There is a lot of stuff here. I hope we can simplify to just a few later that's absolutely needed for production. Move test-related variables to a separate example file?

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Removed the unused variables and created a test object.
Could also remove the non Harmony URL's.
For now I've left them in, in case we want to deploy to other networks.

ROPSTEN_URL=https://eth-ropsten.alchemyapi.io/v2/<YOUR ALCHEMY KEY>
PRIVATE_KEY=0xabc123abc123abc123abc123abc123abc123abc123abc123abc123abc123abc1
4 changes: 4 additions & 0 deletions chaincode/.eslintignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
node_modules
artifacts
cache
coverage
24 changes: 24 additions & 0 deletions chaincode/.eslintrc.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
module.exports = {
env: {
browser: false,
es2021: true,
mocha: true,
node: true,
},
plugins: ["@typescript-eslint"],
extends: [
"standard",
"plugin:prettier/recommended",
"plugin:node/recommended",
],
parser: "@typescript-eslint/parser",
parserOptions: {
ecmaVersion: 12,
},
rules: {
"node/no-unsupported-features/es-syntax": [
"error",
{ ignores: ["modules"] },
],
},
};
13 changes: 13 additions & 0 deletions chaincode/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
node_modules
.env
coverage
coverage.json
typechain

#Hardhat files
cache
artifacts

# Additional files
docs
data
3 changes: 3 additions & 0 deletions chaincode/.npmignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
hardhat.config.ts
scripts
test
5 changes: 5 additions & 0 deletions chaincode/.prettierignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
node_modules
artifacts
cache
coverage*
gasReporterOutput.json
1 change: 1 addition & 0 deletions chaincode/.prettierrc
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
{}
7 changes: 7 additions & 0 deletions chaincode/.solhint.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
{
"extends": "solhint:recommended",
"rules": {
"compiler-version": ["error", "^0.8.0"],
"func-visibility": ["warn", { "ignoreConstructors": true }]
}
}
1 change: 1 addition & 0 deletions chaincode/.solhintignore
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
node_modules
18 changes: 18 additions & 0 deletions chaincode/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
# AssetManager

Smart contract functionality for managing assets for the sms-wallet.
For an overview read the [wiki](https://github.com/polymorpher/sms-wallet/wiki#sms-controlled-mini-wallet).

## Developers

Quickstart
```
git clone https://github.com/polymorpher/sms-wallet
cd chaincode
yarn
yarn test
```




234 changes: 234 additions & 0 deletions chaincode/contracts/AssetManager.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,234 @@
// SPDX-License-Identifier: UNLICENSED

pragma solidity ^0.8.9;

// import "hardhat/console.sol";
import "@openzeppelin/contracts/access/Ownable.sol";
import "@openzeppelin/contracts/token/ERC20/ERC20.sol";
import "@openzeppelin/contracts/token/ERC721/ERC721.sol";
import "@openzeppelin/contracts/token/ERC1155/ERC1155.sol";
import "./Enums.sol";

contract AssetManager is Ownable {
johnwhitton marked this conversation as resolved.
Show resolved Hide resolved
johnwhitton marked this conversation as resolved.
Show resolved Hide resolved
event DepositSuccesful(address indexed user, uint256 amount, uint256 balance);
event WithdrawalSuccesful(address indexed user, uint256 amount, uint256 balance);
error WithdrawalFailed(
address user,
uint256 amount,
uint256 balance,
string reason
);
event AuthorizationSuccesful(address indexed user, uint256 newLimit, uint256 userBalance);
error AuthorizationFailed(
address depositor,
uint256 userBalance,
uint256 limit,
string reason
);
event SendSuccesful(address indexed from, address indexed to, uint256 amount, uint256 newBalance, uint256 newLimit);
error SendFailed(
address from,
address to,
uint256 amount,
uint256 balance,
uint256 limit,
string reason
);
event TransferSuccesful(uint256 amount,
Enums.TokenType tokenType,
uint256 tokenId,
address tokenAddress,
address indexed from,
address indexed to);
error TransferFailed(
uint256 amount,
Enums.TokenType tokenType,
uint256 tokenId,
address tokenAddress,
address from,
address to,
string reason
);

address private _operator;
mapping(address => uint256) public userBalances;
mapping(address => uint256) public userAuthorizations;

constructor(address operator_) {
setOperator(operator_);
}

modifier onlyOperator {
require(msg.sender == _operator, "Can only be called by Operator");
_;
}

modifier onlyOwnerOrOperator {
bool ownerOperator = ((msg.sender == owner()) || (msg.sender == operator()));
require(ownerOperator, "Can only be called by Owner or Operator");
_;
}

/**
* @dev Returns the address of the current operator.
*/
function operator() public view virtual returns (address) {
johnwhitton marked this conversation as resolved.
Show resolved Hide resolved
return _operator;
}

function setOperator(address operator_) onlyOwnerOrOperator public {
_operator = operator_;
}

function deposit() public payable {
userBalances[address(msg.sender)] += msg.value;
// update the userBalance
emit DepositSuccesful(
msg.sender,
msg.value,
userBalances[address(msg.sender)]
);
}

function withdraw(uint256 amount) public payable {
uint256 balance = userBalances[address(msg.sender)];
// if zero is passed withdraw all funds
if (amount == 0){ amount = balance; }
// check msg.senders balance
if (amount > balance) {
johnwhitton marked this conversation as resolved.
Show resolved Hide resolved
revert WithdrawalFailed(
msg.sender,
amount,
balance,
"Insufficient Locked Funds to Withdraw "
);
}

// withdraw funds from the contract (update userBalance before transfer to protect from reentracy attack)
uint256 newBalance = balance - amount;
userBalances[address(msg.sender)] = newBalance;
payable(msg.sender).transfer(amount);

// update the authorized amount.
uint256 currentLimit = this.userAuthorizations(address(msg.sender));
if (currentLimit > newBalance ) { authorize(newBalance); }
johnwhitton marked this conversation as resolved.
Show resolved Hide resolved

// update the userBalance
emit WithdrawalSuccesful(
msg.sender,
amount,
userBalances[address(msg.sender)]
);
}

function authorize(uint256 limit) public {
// check msg.senders balance
uint256 balance = this.userBalances(address(msg.sender));
// uint256 currentLimit = getUserAuthorization(msg.sender);
// uint256 newLimit = currentLimit + limit;
if (balance < limit) {
revert AuthorizationFailed(
msg.sender,
balance,
limit,
"New limt amount greater than users balance"
);
}

userAuthorizations[address(msg.sender)] = limit;

// update the userBalance
emit AuthorizationSuccesful(
msg.sender,
limit,
balance
);
}
function send(uint256 amount, address from, address to) public onlyOperator() {
uint256 balance = userBalances[from];
uint256 limit = userAuthorizations[from];
if (amount == 0) {
revert SendFailed(
from,
to,
amount,
balance,
limit,
"Send amount cannot equal 0"
);
}
// check from balance
if (amount > balance) {
johnwhitton marked this conversation as resolved.
Show resolved Hide resolved
revert SendFailed(
from,
to,
amount,
balance,
limit,
"Insufficient Locked Funds to Send "
);
}
// check from balance
if (amount > limit) {
revert SendFailed(
from,
to,
amount,
balance,
limit,
"Insufficient Authorized Funds to Send "
);
}
// withdraw funds from the contract (update userBalance before transfer to protect from reentracy attack)
uint256 newBalance = balance - amount;
userBalances[address(from)] = newBalance;

// update the authorized amount.
uint256 newLimit = limit - amount;
userAuthorizations[address(from)] = newLimit;

payable(to).transfer(amount);

emit SendSuccesful(
from,
to,
amount,
newBalance,
newLimit
);
}
function transfer( uint256 amount, Enums.TokenType tokenType, uint256 tokenId, address tokenAddress, address from, address to) public onlyOperator {
if ( tokenType == Enums.TokenType.ERC20 ) {
bool success = ERC20(tokenAddress).transferFrom(from, to, amount);
if (success) {
emit TransferSuccesful(amount, tokenType, tokenId, tokenAddress, from, to);
} else {
revert TransferFailed(
amount,
tokenType,
tokenId,
tokenAddress,
from,
to,
"Invalid tokenType "
);
}
} else if ( tokenType == Enums.TokenType.ERC721 ) {
ERC721(tokenAddress).safeTransferFrom(from, to, tokenId);
emit TransferSuccesful(amount, tokenType, tokenId, tokenAddress, from, to);
} else if ( tokenType == Enums.TokenType.ERC1155 ) {
ERC1155(tokenAddress).safeTransferFrom(from, to, tokenId, amount, "");
emit TransferSuccesful(amount, tokenType, tokenId, tokenAddress, from, to);
} else {
revert TransferFailed(
amount,
tokenType,
tokenId,
tokenAddress,
from,
to,
"Invalid tokenType "
);
}
}
}
9 changes: 9 additions & 0 deletions chaincode/contracts/Enums.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
// SPDX-License-Identifier: UNLICENSED

pragma solidity ^0.8.9;

library Enums {
enum TokenType{
ERC20, ERC721, ERC1155, NONE
}
}
Loading