-
Notifications
You must be signed in to change notification settings - Fork 4
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
ws-miniwallet-v0.1 MiniWallet Functionality #17
Conversation
Below Commit: Summary of changes
Second Commit: Summary of Changes
|
Below Commit: Summary of Changes
|
miniserver/abi/MiniWallet.json
Outdated
{ | ||
"_format": "hh-sol-artifact-1", |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
wrong content. This should be abi only
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The code was written to use the full hardhat generated json and take the abi from it see here
i.e. miniWallet = new ethers.Contract(networkConfig.miniWalletAddress, MiniWallet.abi, provider)
We can change to use abi only however when branching from main we received the error
UncaughtException [SyntaxError: /Users/john/one-wallet/sms-wallet/miniserver/abi/MiniWallet.json: Unexpected token , in JSON at position 20047].
node:internal/process/per_thread:221
throw errnoException(err, 'kill');
^
If we prefer to use abi only need to ensure that it's JSON compliant and/or update the retrieval mechanism above.
Also should automate population of this folder when doing a yarn compile
in miniwallet
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Have updated the logic to use abi only
abi's are populated when compiling using hardhat-abi-exporter
The configuration is in hardhat.config.ts
abiExporter: {
path: '../miniserver/abi',
runOnCompile: true,
clear: true,
flat: true,
only: [':MiniWallet$', ':MiniProxy$'],
format: 'json',
spacing: 2
},
Note: considered moving the abi's under a shared folder however left as miniserver only for now.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
there is an extra ,
which I forgot to remove, just remove from the end of the JSON
miniserver/routes/index.js
Outdated
// Allow requesting of funds from 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"`) | ||
} | ||
toAddress = checkSumAddress(smsParams[1]) | ||
funderAddress = checkSumAddress(smsParams[1]) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
smsParams[1] is destination address, not "funder" (from)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
In the current logic
a. the requestor is driven by the phone number
b. the funder is driven by the phone number of address.
The call to the send function is as follows
function send(
uint256 amount,
address from,
address to
testing uses the twilio cli as follows
'twilio api:core:messages:create --from "+14158401410" --to "+17372327333" --body "p 4158401410 0.1"'
So current mapping is
sms params 0 : drives the transaction type i.e. p maps to send
sms params 1 : is the funder address (i.e. from in the send function)
sending phone number: is the to (i.e. the to in the send function)
sms params 2 : is the amount (i.e amount in the send function)
We can change this mapping if needed or document this somewhere to make it more clear.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@polymorpher can you confirm that you are happy with the above mapping.
If not can you let me know the changes or documentation that is required 🙏
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Not sure whether we are on the same page of what "funder" implies? Is it not the person who initiated and funded the transaction? If so, why is smsParams[1] not the phone number of the user whom the initiator is paying to?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Sorry for the confusion. Have updated now so that the user requests the payment.
Yes the funder is the person who funded the transaction (the user) and sends the payment request.
But the funding request (payment) is initiated by the requestor (the creator).
I originally had this confused as well.
This is why we use the above mappings.
Let me try and clarify the above by adding a little more context and using the terms user
and creator
user
(also known as funder): someone who registers their phone and makes deposits into the miniwallet and approves those funds to be used by the creator.. For testing we use- Phone:
+14158401410
- Address:
0x143A933E79931006b3Eb89cBc938587546faF159
- Phone:
creator
(also known as the requestor) : the game developer/artist who registers their phone and requests the operator to send the users approved funds . For Testing we use- Phone:
+16505473175
- Address:
0x58bB8c7D2c90dF970fb01a5cD29c4075C41d3FFB
- Caller:
TOKEN WARRIOR
(used when constructing contract calls via https://demo.smswallet.xyz)
- Phone:
The complete scenario is as follows
- The user (funder) and creator(requestor) register their phones.
- The user (funder) transfers 40 ONE into their account.
- The user(funder) deposits 10 ONE into the miniwallet and approves the creator to withdraw some of these funds (1 ONE).
- This can be triggered by a request url such as this.
- Which can be created by using the demo request page
- This gives the user a request which looks as follows (once again depositing funds into the mini-wallet and approving some of those funds to be used by the creator).
- The user (funder) can then check their minwallet balance by text
b
from their phone (+14158401410)
- We test this using the twilio command
twilio api:core:messages:create --from "+14158401410" --to "+17372327333" --body "b"
- And receive the following response
Your balance is 10.0 (address: 0x143a933e79931006b3eb89cbc938587546faf159)
5. The creator (requestor) can than ask the operator to send them some funds on behalf of the user(funder) by sending a message from their phone (+16505473175)
We test this using the twilio commandtwilio api:core:messages:create --from "+16505473175" --to "+17372327333" --body "p 4158401410 0.1"
And receive the following responseSent 0.1 (from: 0x143a933e79931006b3eb89cbc938587546faf159, to 0x58bb8c7d2c90df970fb01a5cd29c4075c41d3ffb, transaction:0x83b98c966385060236d7d7c4c7eff0d69fade1912e652c8efd24d533c20450ea)
- The user (funder) can than ask the operator to send them some funds on behalf from the miniwallet by sending a message from their phone (+14158401410)
- We test this using the twilio command
twilio api:core:messages:create --from "+14158401410" --to "+17372327333" --body "p 6505473175 0.1"
- And receive the following response
Sent 0.1 (from: 0x143a933e79931006b3eb89cbc938587546faf159, to 0x58bb8c7d2c90df970fb01a5cd29c4075c41d3ffb, transaction:0x83b98c966385060236d7d7c4c7eff0d69fade1912e652c8efd24d533c20450ea)
- The creator (requestor) balance is now increased by 0.1
- The creator (requestor) still has no funds held in the miniwallet (the funds were sent from the miniwallet to their address)
- We test the creator checking their miniwallet balance using the twilio command (i.e. from the creator (requestor))
twilio api:core:messages:create --from "+16505473175" --to "+17372327333" --body "b"
- and receive the response
- `Your balance is 0.0 (address: 0x58bb8c7d2c90df970fb01a5cd29c4075c41d3ffb)
- The user (funders) miniwallet balance is now decreased
-
- We test this using the twilio command
twilio api:core:messages:create --from "+14158401410" --to "+17372327333" --body "b"
- And receive the following response
Your balance is 9.9 (address: 0x143a933e79931006b3eb89cbc938587546faf159)
- The user (funder) sms-wallet balance after the funding and approval is as follows
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
After discussion with @polymorpher
The requirement is that the user calls the payment function (not the creator).
Will update the codebase accordingly.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Have updated the codebase so that user now sends payment requests.
Also updated workflow above and testing documents in FEATURE_ROLLOUT.md
initialAuthLimit: ethers.utils.parseEther(process.env.ETH_LOCAL_MINIWALLET_INIITIAL_AUTH_LIMIT || '100') | ||
} | ||
}, | ||
hardhat: { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
what's the difference between ethLocal and hardhat config?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
ethLocal is used for end to end testing. It is part or the local testing environment documented here specifically using ganache as documented here
hardhat config is used by the following commands
- yarn test or
npx hardhat test
- yarn deploy or
npx hardhat deploy --tags deploy
Note the tags are populated in each of the test scripts
e.g. in 001_deploy_miniWallet.ts we use the following tags deployFunction.tags = ['MiniWallet', 'deploy', 'MiniWalletHardhatDeploy']
There are additional control mechanisms which can be leveraged for sequencing if needed.
bytes32 public constant MINTER_ROLE = keccak256("MINTER_ROLE"); | ||
|
||
constructor(uint256[] memory tokenIds, uint256[] memory amounts) | ||
ERC1155("ipfs://testerc1155.modulo.so/") |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
that's invalid ipfs address
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Understood.
This does not impact testing and is used as a placeholder only.
If we have a preferred ipfs adress and I can update.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@polymorpher Can you provide a better (valid) ipfs url which you’d like me to use instead of ipfs://testerc1155.modulo.so/
for example for the valid URI’s used for the mini721 and mini1155 are
MINI721_DEPLOY_BASE_URI=ipfs://Qmc8DVEthq7cZMTMyZ2NQ8dHkG99n549DMBwNzAypQgXe1/Mini721/
and
MINI1155_DEPLOY_BASE_URI=ipfs://Qmc8DVEthq7cZMTMyZ2NQ8dHkG99n549DMBwNzAypQgXe1/Mini1155/
would you like me to hardcode these values?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
if the test data is not populated at an ipfs location already, empty seems more reasonable
override(ERC1155, AccessControl) | ||
returns (bool) | ||
{ | ||
return super.supportsInterface(interfaceId); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
super calling wrong parent. And here it should support both ERC1155 and AccessControl interfaces instead of just one
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is a simple ERC1155 contract generated from the open zeppelin wizard
supporting
- mintable
- updateable uri's
- roles
If we need to support above requirements ERC1155 and AccessControl interfaces instead of just one
we can manually write the mock contracts.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks for identifying this @polymorpher 🙏
Further research confirmed your feedback and I have replaced supportsInterface with code below
function supportsInterface(bytes4 interfaceId)
public
view
override(ERC1155, AccessControl)
returns (bool)
{
return (ERC1155.supportsInterface(interfaceId) ||
AccessControl.supportsInterface(interfaceId));
}
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
they need to fix it
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
As stated above I have corrected the code for our mock Tokens.
I have also raised OpenZeppelin/contracts-wizard#183 for Open Zeppelin to fix the wizard.
override(ERC721, AccessControl) | ||
returns (bool) | ||
{ | ||
return super.supportsInterface(interfaceId); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
same issue here
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
same as above
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks for identifying this @polymorpher 🙏
Further research confirmed your feedback and I have replaced supportsInterface with code below
function supportsInterface(bytes4 interfaceId)
public
view
override(ERC721, AccessControl)
returns (bool)
{
return (ERC721.supportsInterface(interfaceId) ||
AccessControl.supportsInterface(interfaceId));
}
} | ||
|
||
function _baseURI() internal pure override returns (string memory) { | ||
return "ipfs://testerc721.modulo.so/"; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
same issue here
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
same as above
miniwallet/test/utilities/index.ts
Outdated
@@ -3,6 +3,10 @@ import { expect } from 'chai' | |||
import { MockProvider } from 'ethereum-waffle' | |||
import { Contract, BigNumber } from 'ethers' | |||
import { ethers } from 'hardhat' | |||
// TODO Parameterize CallData | |||
const ContractPath = '../../build/contracts/miniWallet/miniWallet.sol/MiniWallet.json' |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
should merge into one line
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
in fact it is not needed since you only need signature for initialize function
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This can be covered as stated here
// TODO Parameterize CallData
The calldata includes both the signature for the initialize function as well as the parameters see here which is using the passed paramaters from here
i.e.
[
config.test.miniWallet.initialOperatorThreshold,
config.test.miniWallet.initialOperators,
config.test.miniWallet.initialUserLimit,
config.test.miniWallet.initialAuthLimit
]
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Have changed this to use the following line
const calldata = contract[1].interface.encodeFunctionData('initialize', contract[2] || [])
Note: all Proxy contracts we are deploying will use the initialize
function so can hardcode this for now.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
okay
miniwallet/test/utilities/index.ts
Outdated
const implementation = await contract[1].deploy() | ||
await implementation.deployed() | ||
const iface = new ethers.utils.Interface(abi) | ||
const calldata = iface.encodeFunctionData('initialize', contract[2] || []) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
could use generic encoding function similar to one-wallet lib util encodeCalldata
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Great point 🙏
// WARNING: returns string-encoded bytes (0x....), unlike other functions provided in this package
encodeCalldata: (method, values = []) => {
if (!method) {
return '0x'
}
const selector = abi.encodeFunctionSignature(method)
const m = method.match(/.+\((.*)\)/)
if (!m) {
return null
}
const params = m[1] ? m[1].split(',') : []
const encodedParameters = abi.encodeParameters(params, values)
return selector + encodedParameters.slice(2)
},
We should consider how it is best to leverage the existing one-wallet lib.
Perhaps writing this as an npm package may be one solution.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I reviewed the oneWallet encodCallData above.
If I understand it correctly if would need
method = 'initialize(uint8, address[], uint256, uint256)
params = [
config.test.miniWallet.initialOperatorThreshold,
config.test.miniWallet.initialOperators,
config.test.miniWallet.initialUserLimit,
config.test.miniWallet.initialAuthLimit
]
And used this one-liner which is generic for all Proxy deploys and Implementation upgrades
const calldata = contract[1].interface.encodeFunctionData('initialize', contract[2] || [])
which translates to
const calldata = contract[1].interface.encodeFunctionData(
'initialize',
[
config.test.miniWallet.initialOperatorThreshold,
config.test.miniWallet.initialOperators,
config.test.miniWallet.initialUserLimit,
config.test.miniWallet.initialAuthLimit
]
The encodeFunctionData
seems slightly simpler as it just needs the method
and will figure out the data types
Moving forward could create a generic function taking input as (abi, method, params) or alternately contract instance, method params) if needed.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
okay, importing the whole abi seems excessive if it is just for getting the signature of initialize function. It is known anyway
@@ -46,7 +46,7 @@ POLLING_INTERVAL=1000 | |||
DEFAULT_NETWORK='eth-local' | |||
GAS_LIMIT='67219000' | |||
#GAS_PRICE='10000000' | |||
GAS_PRICE='413290302' | |||
GAS_PRICE='529942254' |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
should use type 2 transactions instead of arbitrary numbers, or manually set base fee in emulating local eth blockchain
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Good Point 🙏
An example of how to use type 2 transactions
would be useful
Initial google shows these EIPS
- EIP-1559: A transaction pricing mechanism that includes fixed-per-block network fee that is burned and dynamically expands/contracts block sizes to deal with transient congestion.
- EIP-2178: TransactionType || TransactionPayload is a valid transaction and TransactionType || ReceiptPayload is a valid transaction receipt where TransactionType identifies the format of the transaction and *Payload is the transaction/receipt contents, which are defined in future EIPs.
Will investigate further.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Further Investigation
Gives this code example
require("log-timestamp");
const ethers = require("ethers");
const privateKey = ("ADD_YOUR_PRIVATE_KEY_HERE").toString('hex');
const wallet = new ethers.Wallet(privateKey);
const address = wallet.address;
console.log("Public Address:", address);
const httpsUrl = "ADD_YOUR_HTTP_URL_HERE";
console.log("HTTPS Target", httpsUrl);
const init = async function () {
const httpsProvider = new ethers.providers.JsonRpcProvider(httpsurl);
let nonce = await httpsProvider.getTransactionCount(address);
console.log("Nonce:", nonce);
let feeData = await httpsProvider.getFeeData();
console.log("Fee Data:", feeData);
const tx = {
type: 2,
nonce: nonce,
to: "0x8D97689C9818892B700e27F316cc3E41e17fBeb9", // Address to send to
maxPriorityFeePerGas: feeData["maxPriorityFeePerGas"], // Recommended maxPriorityFeePerGas
maxFeePerGas: feeData["maxFeePerGas"], // Recommended maxFeePerGas
value: ethers.utils.parseEther("0.01"), // .01 ETH
gasLimit: "21000", // basic transaction costs exactly 21000
chainId: 42, // Ethereum network id
};
console.log("Transaction Data:", tx);
const signedTx = await wallet.signTransaction(tx);
console.log("Signed Transaction:", signedTx);
const txHash = ethers.utils.keccak256(signedTx);
console.log("Precomputed txHash:", txHash);
console.log(`https://kovan.etherscan.io/tx/${txHash}`);
httpsProvider.sendTransaction(signedTx).then(console.log);
};
init();
Also debugging eth_estimateGas
implies that we are using type2 transactions "type":"0x2",
requestBody: '{"method":"eth_estimateGas","params":[{"type":"0x2","maxFeePerGas":"0x77359400","maxPriorityFeePerGas":"0x77359400","from":"0xf39fd6e51aad88f6f4ce6ab8827279cfffb92266","to":"0xe7f1725e7734ce288f8367e1bb143e90bb3f0512","data":"0x3d0594d4000000000000000000000000000000000000000000000000016345785d8a0000000000000000000000000000143a933e79931006b3eb89cbc938587546faf15900000000000000000000000058bb8c7d2c90df970fb01a5cd29c4075c41d3ffb"}],"id":49,"jsonrpc":"2.0"}',
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
For now I've updated prepareExecute
to retrieve the gasPrice
const gasPrice = await provider.getGasPrice()
This introduces an additional rpc call on each transaction
Note this uses getGasPrice there is also estimateGas which may give a more accurate estimate.
@polymorpher let me know your thoughts on this and whether the additional rpc call is to burdensome, and if you'd prefer to use estimateGas
Also assumption is that this works on all evm compatible chains (Including Harmony) however this has only been tested on hardhat and ganache.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
that's fine, type 2 is not supported on all chains, including ganache
This PR is now at a logical point to merge.
|
miniserver/routes/index.js
Outdated
@@ -39,47 +39,50 @@ const parseSMS = async (req, res, next) => { | |||
res.status(status).send(response.toString()) | |||
} | |||
try { | |||
let fromAddress |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
no reason this should be mutable
Initial Commit: Summary of Changes
miniwallet/build/contracts/miniWallet/MiniWAllet.sol/MiniWallet.json
tominiserver/abi/MiniWallet.json
ethlocal
which is used for validation and is separate fromtest
which is used inyarn test
initialOperatorThreshold
ethlocal
to resolve this errorTransaction's maxFeePerGas (413290302) is less than the block's baseFeePerGas (529942254) (vm hf=london -> block -> tx)