Skip to content

Commit

Permalink
Merge pull request #17 from polymorpher/ws-miniwallet-v0.1
Browse files Browse the repository at this point in the history
ws-miniwallet-v0.1 MiniWallet Functionality
  • Loading branch information
polymorpher authored Sep 24, 2022
2 parents 7f27f3f + dc54f9d commit 35b30d5
Show file tree
Hide file tree
Showing 25 changed files with 362 additions and 641 deletions.
2 changes: 1 addition & 1 deletion miniserver/.env.example
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@ POLLING_INTERVAL=1000
DEFAULT_NETWORK='eth-local'
GAS_LIMIT='67219000'
#GAS_PRICE='10000000'
GAS_PRICE='413290302'
GAS_PRICE='529942254'
CACHE='cache'
STATS_PATH='../data/stats.json'
OTP_INTERVAL=60000
Expand Down
84 changes: 84 additions & 0 deletions miniserver/abi/MiniProxy.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
[
{
"inputs": [
{
"internalType": "address",
"name": "_logic",
"type": "address"
},
{
"internalType": "bytes",
"name": "_data",
"type": "bytes"
}
],
"stateMutability": "payable",
"type": "constructor"
},
{
"anonymous": false,
"inputs": [
{
"indexed": false,
"internalType": "address",
"name": "previousAdmin",
"type": "address"
},
{
"indexed": false,
"internalType": "address",
"name": "newAdmin",
"type": "address"
}
],
"name": "AdminChanged",
"type": "event"
},
{
"anonymous": false,
"inputs": [
{
"indexed": true,
"internalType": "address",
"name": "beacon",
"type": "address"
}
],
"name": "BeaconUpgraded",
"type": "event"
},
{
"anonymous": false,
"inputs": [
{
"indexed": true,
"internalType": "address",
"name": "implementation",
"type": "address"
}
],
"name": "Upgraded",
"type": "event"
},
{
"stateMutability": "payable",
"type": "fallback"
},
{
"inputs": [],
"name": "implementation",
"outputs": [
{
"internalType": "address",
"name": "impl",
"type": "address"
}
],
"stateMutability": "view",
"type": "function"
},
{
"stateMutability": "payable",
"type": "receive"
}
]
2 changes: 1 addition & 1 deletion miniserver/abi/MiniWallet.json
Original file line number Diff line number Diff line change
Expand Up @@ -1043,4 +1043,4 @@
"stateMutability": "nonpayable",
"type": "function"
}
],
]
7 changes: 5 additions & 2 deletions miniserver/blockchain.js
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ const init = async () => {
Logger.log('Initializing blockchain for server')
Logger.log(`network=${config.defaultNetwork}, ${JSON.stringify(networkConfig)}`)
provider = ethers.getDefaultProvider(networkConfig.url)
miniWallet = new ethers.Contract(networkConfig.miniWalletAddress, MiniWallet.abi, provider)
miniWallet = new ethers.Contract(networkConfig.miniWalletAddress, MiniWallet, provider)
provider.pollingInterval = config.pollingInterval
signers.splice(0, signers.length)
if (networkConfig.mnemonic) {
Expand Down Expand Up @@ -72,11 +72,14 @@ const sampleExecutionAddress = () => {

// basic executor used to send funds
const prepareExecute = (logger = Logger.log, abortUnlessRPCError = true) => async (method, ...params) => {
console.log(`method: ${method}`)
console.log(`params: ${JSON.stringify(params)}`)
const fromIndex = sampleExecutionAddress()
const from = signers[fromIndex].address
const miniWalletSigner = miniWallet.connect(signers[fromIndex])
logger(`Sampled [${fromIndex}] ${from}`)
const latestNonce = await rpc.getNonce({ address: from, network: config.defaultNetwork })
const gasPrice = await provider.getGasPrice()
const snapshotPendingNonces = pendingNonces[from]
const nonce = latestNonce + snapshotPendingNonces
pendingNonces[from] += 1
Expand All @@ -89,7 +92,7 @@ const prepareExecute = (logger = Logger.log, abortUnlessRPCError = true) => asyn
const tx = await backOff(
async () => miniWalletSigner[method](...params, {
nonce,
gasPrice: ethers.BigNumber.from(config.gasPrice).mul((numAttempts || 0) + 1),
gasPrice: gasPrice.mul((numAttempts || 0) + 1),
value: 0,
}), {
retry: (ex, n) => {
Expand Down
22 changes: 11 additions & 11 deletions miniserver/routes/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -39,47 +39,47 @@ const parseSMS = async (req, res, next) => {
res.status(status).send(response.toString())
}
try {
const u = await User.findByPhone({ phone: senderPhoneNumber })
if (!u) {
const { address: fromAddress } = (await User.findByPhone({ phone: senderPhoneNumber })) || {}
if (!fromAddress) {
return respond('You are not registered. Signup at https://smswallet.xyz')
}
if (smsParams.length < 1) {
return respond('error: empty sms command')
}
if (smsParams[0] === 'b') {
req.processedBody = { ...req.processedBody, command: 'balance', fromAddress: u.address }
req.processedBody = { ...req.processedBody, command: 'balance', fromAddress }
return next()
}
if (smsParams[0] === 'p') {
if (smsParams.length < 2) {
return respond('error: pay request requires funder and an amount. example request "p +14158401999 0.1"')
return respond('error: pay request requires recipient and an amount. example request "p +16505473175 0.1"')
}
if (!(toNumber(smsParams[2]) > 0)) {
return respond(`error: pay request requires a valid amount': ${smsParams[2]} example request "p +14158401999 0.1"`)
return respond(`error: pay request requires a valid amount': ${smsParams[2]} example request "p +116505473175 0.1"`)
}
const amount = ethers.utils.parseEther(smsParams[2])
let toAddress
// Allow requesting of funds from users by address (without checking registered phone number)
// Allow sending of funds to users by address (without checking registered phone number)
if (smsParams[1].substr(0, 2) === '0x') {
if (!isValidAddress(smsParams[1])) {
return respond(`error: invalid funder address ${smsParams[1]}. example request "p 0x8ba1f109551bd432803012645ac136ddd64dba72 0.1"`)
return respond(`error: invalid recipient address ${smsParams[1]}. example request "p 0x58bB8c7D2c90dF970fb01a5cD29c4075C41d3FFB 0.1"`)
}
toAddress = checkSumAddress(smsParams[1])
} else {
const { isValid, phoneNumber } = phone(smsParams[1], smsParams[1] === '+' ? undefined : phone(u.phone).countryIso3)
if (!isValid) {
return respond(`error: invalid recipient phone number ${smsParams[1]}. example request "p +14158401999 0.1"`)
return respond(`error: invalid recipient phone number ${smsParams[1]}. example request "p +16505473175 0.1"`)
}
const u2 = await User.findByPhone({ phone: phoneNumber })
if (!u2?.address) {
return respond(`error: funders phone number is not a registered wallet: ${smsParams[1]}. example request "p +14158401999 0.1"`)
return respond(`error: recipients phone number is not a registered wallet: ${smsParams[1]}. example request "p +16505473175 0.1"`)
}
toAddress = u2.address
}
req.processedBody = { ...req.processedBody, command: 'pay', fromAddress: u.address, toAddress, amount }
req.processedBody = { ...req.processedBody, command: 'pay', fromAddress, toAddress, amount }
return next()
}
return respond('error: invalid sms command. example payment request "p +14158401999 0.1"')
return respond('error: invalid sms command. example payment request "p +16505473175 0.1"')
} catch (ex) {
console.error(ex)
return respond('An unexpected occurred. Please contact support.')
Expand Down
17 changes: 12 additions & 5 deletions miniwallet/.env.example
Original file line number Diff line number Diff line change
Expand Up @@ -19,9 +19,16 @@ MINIWALLET_INITIAL_OPERATORS=["0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266","0x70
MINIWALLET_INITIAL_USER_LIMIT=1000000
MINIWALLET_INITIAL_AUTH_LIMIT=100000

# Smart Contract Testing config
# Smart Contract ethlocal config
## MiniWallet
TEST_MINIWALLET_INITIAL_OPERATOR_THRESHOLD=10
TEST_MINIWALLET_INITIAL_OPERATORS=["0x70997970C51812dc3A010C7d01b50e0d17dc79C8","0x3C44CdDdB6a900fa2b585dd299e03d12FA4293BC","0x90F79bf6EB2c4f870365E785982E1f101E93b906"]
TEST_MINIWALLET_INITIAL_USER_LIMIT=1000
TEST_MINIWALLET_INITIAL_AUTH_LIMIT=100
ETH_LOCAL_MINIWALLET_INITIAL_OPERATOR_THRESHOLD=10
ETH_LOCAL_MINIWALLET_INITIAL_OPERATORS=["0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266","0x70997970C51812dc3A010C7d01b50e0d17dc79C8","0x3C44CdDdB6a900fa2b585dd299e03d12FA4293BC","0x90F79bf6EB2c4f870365E785982E1f101E93b906"]
ETH_LOCAL_MINIWALLET_INITIAL_USER_LIMIT=1000
ETH_LOCAL_MINIWALLET_INITIAL_AUTH_LIMIT=100

# Smart Contract hardhat config (used for testing e.g. in `yarn test`)
## MiniWallet
HARDHAT_MINIWALLET_INITIAL_OPERATOR_THRESHOLD=10
HARDHAT_MINIWALLET_INITIAL_OPERATORS=["0x70997970C51812dc3A010C7d01b50e0d17dc79C8","0x3C44CdDdB6a900fa2b585dd299e03d12FA4293BC","0x90F79bf6EB2c4f870365E785982E1f101E93b906"]
HARDHAT_MINIWALLET_INITIAL_USER_LIMIT=1000
HARDHAT_MINIWALLET_INITIAL_AUTH_LIMIT=100
11 changes: 4 additions & 7 deletions miniwallet/config.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,14 +3,11 @@ import 'dotenv/config'

export default {
test: {
operator: JSON.parse(process.env.TEST_MINIWALLET_INITIAL_OPERATORS || '[]')[0] || '0x70997970C51812dc3A010C7d01b50e0d17dc79C8',
user: process.env.TEST_USER || '0xEf4634BdBc6F6528EacB49278d7E17BCB9e2689A',
creator: process.env.TEST_CREATOR || '0x1cf6490889A92371fdBC610C4A862061F28BaFfA',
miniWallet: {
initialOperatorThreshold: process.env.TEST_MINIWALLET_INITIAL_OPERATOR_THRESHOLD,
initialOperators: JSON.parse(process.env.TEST_MINIWALLET_INITIAL_OPERATORS || '[]'),
initialUserLimit: ethers.utils.parseEther(process.env.TEST_MINIWALLET_INIITIAL_USER_LIMIT || '1000'),
initialAuthLimit: ethers.utils.parseEther(process.env.TEST_MINIWALLET_INIITIAL_AUTH_LIMIT || '100')
initialOperatorThreshold: process.env.HARDHAT_MINIWALLET_INITIAL_OPERATOR_THRESHOLD || '10',
initialOperators: JSON.parse(process.env.HARDHAT_MINIWALLET_INITIAL_OPERATORS || '[]'),
initialUserLimit: ethers.utils.parseEther(process.env.HARDHAT_MINIWALLET_INIITIAL_USER_LIMIT || '1000'),
initialAuthLimit: ethers.utils.parseEther(process.env.HARDHAT_MINIWALLET_INIITIAL_AUTH_LIMIT || '100')
}
}
}
18 changes: 17 additions & 1 deletion miniwallet/config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,26 @@ import 'dotenv/config'
export default {
mainnet: {
miniWallet: {
initialOperatorThreshold: process.env.MINIWALLET_INITIAL_OPERATOR_THRESHOLD,
initialOperatorThreshold: process.env.MINIWALLET_INITIAL_OPERATOR_THRESHOLD || '100',
initialOperators: JSON.parse(process.env.MINIWALLET_INITIAL_OPERATORS || '[]'),
initialUserLimit: ethers.utils.parseEther(process.env.MINIWALLET_INIITIAL_USER_LIMIT || '1000000'),
initialAuthLimit: ethers.utils.parseEther(process.env.MINIWALLET_INIITIAL_AUTH_LIMIT || '100000')
}
},
ethlocal: {
miniWallet: {
initialOperatorThreshold: process.env.ETH_LOCAL_MINIWALLET_INITIAL_OPERATOR_THRESHOLD || '10',
initialOperators: JSON.parse(process.env.ETH_LOCAL_MINIWALLET_INITIAL_OPERATORS || '[]'),
initialUserLimit: ethers.utils.parseEther(process.env.ETH_LOCAL_MINIWALLET_INIITIAL_USER_LIMIT || '1000'),
initialAuthLimit: ethers.utils.parseEther(process.env.ETH_LOCAL_MINIWALLET_INIITIAL_AUTH_LIMIT || '100')
}
},
hardhat: {
miniWallet: {
initialOperatorThreshold: process.env.HARDHAT_MINIWALLET_INITIAL_OPERATOR_THRESHOLD || '10',
initialOperators: JSON.parse(process.env.HARDHAT_MINIWALLET_INITIAL_OPERATORS || '[]'),
initialUserLimit: ethers.utils.parseEther(process.env.HARDHAT_MINIWALLET_INIITIAL_USER_LIMIT || '1000'),
initialAuthLimit: ethers.utils.parseEther(process.env.HARDHAT_MINIWALLET_INIITIAL_AUTH_LIMIT || '100')
}
}
}
57 changes: 57 additions & 0 deletions miniwallet/contracts/mocks/TestERC1155.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
// SPDX-License-Identifier: Apache-2.0

pragma solidity ^0.8.4;

import "@openzeppelin/contracts/token/ERC1155/ERC1155.sol";
import "@openzeppelin/contracts/access/AccessControl.sol";

/// @custom:security-contact [email protected]
contract TestERC1155 is ERC1155, AccessControl {
bytes32 public constant URI_SETTER_ROLE = keccak256("URI_SETTER_ROLE");
bytes32 public constant MINTER_ROLE = keccak256("MINTER_ROLE");

constructor(uint256[] memory tokenIds, uint256[] memory amounts)
ERC1155("")
{
_grantRole(DEFAULT_ADMIN_ROLE, msg.sender);
_grantRole(URI_SETTER_ROLE, msg.sender);
_grantRole(MINTER_ROLE, msg.sender);
for (uint32 i = 0; i < tokenIds.length; i++) {
mint(msg.sender, tokenIds[i], amounts[i], "");
}
}

function setURI(string memory newuri) public onlyRole(URI_SETTER_ROLE) {
_setURI(newuri);
}

function mint(
address account,
uint256 id,
uint256 amount,
bytes memory data
) public onlyRole(MINTER_ROLE) {
_mint(account, id, amount, data);
}

function mintBatch(
address to,
uint256[] memory ids,
uint256[] memory amounts,
bytes memory data
) public onlyRole(MINTER_ROLE) {
_mintBatch(to, ids, amounts, data);
}

// The following functions are overrides required by Solidity.

function supportsInterface(bytes4 interfaceId)
public
view
override(ERC1155, AccessControl)
returns (bool)
{
return (ERC1155.supportsInterface(interfaceId) ||
AccessControl.supportsInterface(interfaceId));
}
}
21 changes: 21 additions & 0 deletions miniwallet/contracts/mocks/TestERC20.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
// SPDX-License-Identifier: Apache-2.0

pragma solidity ^0.8.4;

import "@openzeppelin/contracts/token/ERC20/ERC20.sol";
import "@openzeppelin/contracts/access/AccessControl.sol";

/// @custom:security-contact [email protected]
contract TestERC20 is ERC20, AccessControl {
bytes32 public constant MINTER_ROLE = keccak256("MINTER_ROLE");

constructor(uint256 _amount) ERC20("TestERC20", "T20") {
_mint(msg.sender, _amount * 10**decimals());
_grantRole(DEFAULT_ADMIN_ROLE, msg.sender);
_grantRole(MINTER_ROLE, msg.sender);
}

function mint(address to, uint256 amount) public onlyRole(MINTER_ROLE) {
_mint(to, amount);
}
}
45 changes: 45 additions & 0 deletions miniwallet/contracts/mocks/TestERC721.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
// SPDX-License-Identifier: Apache-2.0

pragma solidity ^0.8.4;

import "@openzeppelin/contracts/token/ERC721/ERC721.sol";
import "@openzeppelin/contracts/access/AccessControl.sol";
import "@openzeppelin/contracts/utils/Counters.sol";

/// @custom:security-contact [email protected]
contract TestERC721 is ERC721, AccessControl {
using Counters for Counters.Counter;

bytes32 public constant MINTER_ROLE = keccak256("MINTER_ROLE");
Counters.Counter private _tokenIdCounter;

constructor(uint256 _amount) ERC721("TestERC721", "T721") {
_grantRole(DEFAULT_ADMIN_ROLE, msg.sender);
_grantRole(MINTER_ROLE, msg.sender);
for (uint32 i = 0; i < _amount; i++) {
safeMint(msg.sender);
}
}

function _baseURI() internal pure override returns (string memory) {
return "";
}

function safeMint(address to) public onlyRole(MINTER_ROLE) {
uint256 tokenId = _tokenIdCounter.current();
_tokenIdCounter.increment();
_safeMint(to, tokenId);
}

// The following functions are overrides required by Solidity.

function supportsInterface(bytes4 interfaceId)
public
view
override(ERC721, AccessControl)
returns (bool)
{
return (ERC721.supportsInterface(interfaceId) ||
AccessControl.supportsInterface(interfaceId));
}
}
Loading

0 comments on commit 35b30d5

Please sign in to comment.