From 1663fc1345011f7de14445bf7c6c8cf746556b5c Mon Sep 17 00:00:00 2001 From: divya Date: Thu, 2 Apr 2020 00:40:23 +0530 Subject: [PATCH 1/6] Demo: Native Meta-tx via Biconomy --- contracts/SapienChildERC20.sol | 27 ++++++- contracts/child/misc/EIP712.sol | 10 +-- demo-script/abi.js | 3 + demo-script/index.js | 130 ++++++++++++++++++++++++++++++++ demo-script/package.json | 17 +++++ 5 files changed, 180 insertions(+), 7 deletions(-) create mode 100644 demo-script/abi.js create mode 100644 demo-script/index.js create mode 100644 demo-script/package.json diff --git a/contracts/SapienChildERC20.sol b/contracts/SapienChildERC20.sol index 9cc2c4b..2981eee 100644 --- a/contracts/SapienChildERC20.sol +++ b/contracts/SapienChildERC20.sol @@ -3,12 +3,13 @@ pragma solidity ^0.5.2; import './child/ChildERC20.sol'; import "./ISapienParentToken.sol"; - contract SapienChildERC20 is ChildERC20 { constructor (address _owner, address _token, string memory _name, string memory _symbol, uint8 _decimals) public - ChildERC20(_owner, _token, _name, _symbol, _decimals) {} + ChildERC20(_owner, _token, _name, _symbol, _decimals) + // EIP712MetaTransaction(_domainName, _version) + {} /// @dev Function that is called when a user or another contract wants to transfer funds. /// @param to Address of token receiver. @@ -18,6 +19,28 @@ contract SapienChildERC20 is ChildERC20 { return transferWithPurpose(to, value, hex""); } + function transferWithPurposeAndSig(address from, address to, uint256 value, bytes memory purpose, + bytes memory sig, bytes32 data, uint256 expiration) public returns (bool) { + + require(expiration == 0 || block.number <= expiration, "Signature is expired"); + + bytes32 dataHash = getTokenTransferOrderHash( + from, + value, + data, + expiration + ); + require(disabledHashes[dataHash] == false, "Sig deactivated"); + disabledHashes[dataHash] = true; + + address _from = ecrecovery(dataHash, sig); + require(from == _from, "signer mistach"); + if (parent != address(0x0) && !ISapienParentToken(parent).beforeTransfer(_from, to, value, purpose)) { + return false; + } + return _transferFrom(from, to, value); + } + /// @dev Function that is called when a user or another contract wants to transfer funds, including a purpose. /// @param to Address of token receiver. /// @param value Number of tokens to transfer. diff --git a/contracts/child/misc/EIP712.sol b/contracts/child/misc/EIP712.sol index 6324616..c0a337e 100644 --- a/contracts/child/misc/EIP712.sol +++ b/contracts/child/misc/EIP712.sol @@ -1,14 +1,14 @@ pragma solidity ^0.5.2; -import { ChainIdMixin } from "../../common/mixin/ChainIdMixin.sol"; +// import { ChainIdMixin } from "../../common/mixin/ChainIdMixin.sol"; -contract LibEIP712Domain is ChainIdMixin { +contract LibEIP712Domain { string constant internal EIP712_DOMAIN_SCHEMA = "EIP712Domain(string name,string version,uint256 chainId,address contract)"; bytes32 constant public EIP712_DOMAIN_SCHEMA_HASH = keccak256(abi.encodePacked(EIP712_DOMAIN_SCHEMA)); - string constant internal EIP712_DOMAIN_NAME = "Matic Network"; + string constant internal EIP712_DOMAIN_NAME = "ropsten"; string constant internal EIP712_DOMAIN_VERSION = "1"; - uint256 constant internal EIP712_DOMAIN_CHAINID = CHAINID; + uint256 constant internal EIP712_DOMAIN_CHAINID = 3; bytes32 public EIP712_DOMAIN_HASH; @@ -45,4 +45,4 @@ contract LibEIP712Domain is ChainIdMixin { } return result; } -} +} \ No newline at end of file diff --git a/demo-script/abi.js b/demo-script/abi.js new file mode 100644 index 0000000..6aeacd0 --- /dev/null +++ b/demo-script/abi.js @@ -0,0 +1,3 @@ +let abi = [ { "inputs": [ { "internalType": "address", "name": "_owner", "type": "address" }, { "internalType": "address", "name": "_token", "type": "address" }, { "internalType": "string", "name": "_name", "type": "string" }, { "internalType": "string", "name": "_symbol", "type": "string" }, { "internalType": "uint8", "name": "_decimals", "type": "uint8" } ], "payable": false, "stateMutability": "nonpayable", "type": "constructor" }, { "anonymous": false, "inputs": [ { "indexed": true, "internalType": "address", "name": "owner", "type": "address" }, { "indexed": true, "internalType": "address", "name": "spender", "type": "address" }, { "indexed": false, "internalType": "uint256", "name": "value", "type": "uint256" } ], "name": "Approval", "type": "event" }, { "anonymous": false, "inputs": [ { "indexed": true, "internalType": "address", "name": "token", "type": "address" }, { "indexed": true, "internalType": "address", "name": "from", "type": "address" }, { "indexed": false, "internalType": "uint256", "name": "amount", "type": "uint256" }, { "indexed": false, "internalType": "uint256", "name": "input1", "type": "uint256" }, { "indexed": false, "internalType": "uint256", "name": "output1", "type": "uint256" } ], "name": "Deposit", "type": "event" }, { "anonymous": false, "inputs": [ { "indexed": true, "internalType": "address", "name": "token", "type": "address" }, { "indexed": true, "internalType": "address", "name": "from", "type": "address" }, { "indexed": true, "internalType": "address", "name": "to", "type": "address" }, { "indexed": false, "internalType": "uint256", "name": "amount", "type": "uint256" }, { "indexed": false, "internalType": "uint256", "name": "input1", "type": "uint256" }, { "indexed": false, "internalType": "uint256", "name": "input2", "type": "uint256" }, { "indexed": false, "internalType": "uint256", "name": "output1", "type": "uint256" }, { "indexed": false, "internalType": "uint256", "name": "output2", "type": "uint256" } ], "name": "LogFeeTransfer", "type": "event" }, { "anonymous": false, "inputs": [ { "indexed": true, "internalType": "address", "name": "token", "type": "address" }, { "indexed": true, "internalType": "address", "name": "from", "type": "address" }, { "indexed": true, "internalType": "address", "name": "to", "type": "address" }, { "indexed": false, "internalType": "uint256", "name": "amount", "type": "uint256" }, { "indexed": false, "internalType": "uint256", "name": "input1", "type": "uint256" }, { "indexed": false, "internalType": "uint256", "name": "input2", "type": "uint256" }, { "indexed": false, "internalType": "uint256", "name": "output1", "type": "uint256" }, { "indexed": false, "internalType": "uint256", "name": "output2", "type": "uint256" } ], "name": "LogTransfer", "type": "event" }, { "anonymous": false, "inputs": [ { "indexed": true, "internalType": "address", "name": "previousOwner", "type": "address" }, { "indexed": true, "internalType": "address", "name": "newOwner", "type": "address" } ], "name": "OwnershipTransferred", "type": "event" }, { "anonymous": false, "inputs": [ { "indexed": true, "internalType": "address", "name": "from", "type": "address" }, { "indexed": true, "internalType": "address", "name": "to", "type": "address" }, { "indexed": false, "internalType": "uint256", "name": "value", "type": "uint256" } ], "name": "Transfer", "type": "event" }, { "anonymous": false, "inputs": [ { "indexed": true, "internalType": "address", "name": "token", "type": "address" }, { "indexed": true, "internalType": "address", "name": "from", "type": "address" }, { "indexed": false, "internalType": "uint256", "name": "amount", "type": "uint256" }, { "indexed": false, "internalType": "uint256", "name": "input1", "type": "uint256" }, { "indexed": false, "internalType": "uint256", "name": "output1", "type": "uint256" } ], "name": "Withdraw", "type": "event" }, { "constant": true, "inputs": [], "name": "EIP712_DOMAIN_HASH", "outputs": [ { "internalType": "bytes32", "name": "", "type": "bytes32" } ], "payable": false, "stateMutability": "view", "type": "function" }, { "constant": true, "inputs": [], "name": "EIP712_DOMAIN_SCHEMA_HASH", "outputs": [ { "internalType": "bytes32", "name": "", "type": "bytes32" } ], "payable": false, "stateMutability": "view", "type": "function" }, { "constant": true, "inputs": [], "name": "EIP712_TOKEN_TRANSFER_ORDER_SCHEMA_HASH", "outputs": [ { "internalType": "bytes32", "name": "", "type": "bytes32" } ], "payable": false, "stateMutability": "view", "type": "function" }, { "constant": true, "inputs": [ { "internalType": "address", "name": "", "type": "address" }, { "internalType": "address", "name": "", "type": "address" } ], "name": "allowance", "outputs": [ { "internalType": "uint256", "name": "", "type": "uint256" } ], "payable": false, "stateMutability": "view", "type": "function" }, { "constant": false, "inputs": [ { "internalType": "address", "name": "", "type": "address" }, { "internalType": "uint256", "name": "", "type": "uint256" } ], "name": "approve", "outputs": [ { "internalType": "bool", "name": "", "type": "bool" } ], "payable": false, "stateMutability": "nonpayable", "type": "function" }, { "constant": true, "inputs": [ { "internalType": "address", "name": "owner", "type": "address" } ], "name": "balanceOf", "outputs": [ { "internalType": "uint256", "name": "", "type": "uint256" } ], "payable": false, "stateMutability": "view", "type": "function" }, { "constant": true, "inputs": [], "name": "decimals", "outputs": [ { "internalType": "uint8", "name": "", "type": "uint8" } ], "payable": false, "stateMutability": "view", "type": "function" }, { "constant": false, "inputs": [ { "internalType": "address", "name": "spender", "type": "address" }, { "internalType": "uint256", "name": "subtractedValue", "type": "uint256" } ], "name": "decreaseAllowance", "outputs": [ { "internalType": "bool", "name": "", "type": "bool" } ], "payable": false, "stateMutability": "nonpayable", "type": "function" }, { "constant": false, "inputs": [ { "internalType": "address", "name": "user", "type": "address" }, { "internalType": "uint256", "name": "amount", "type": "uint256" } ], "name": "deposit", "outputs": [], "payable": false, "stateMutability": "nonpayable", "type": "function" }, { "constant": true, "inputs": [ { "internalType": "bytes32", "name": "", "type": "bytes32" } ], "name": "disabledHashes", "outputs": [ { "internalType": "bool", "name": "", "type": "bool" } ], "payable": false, "stateMutability": "view", "type": "function" }, { "constant": true, "inputs": [ { "internalType": "bytes32", "name": "hash", "type": "bytes32" }, { "internalType": "bytes", "name": "sig", "type": "bytes" } ], "name": "ecrecovery", "outputs": [ { "internalType": "address", "name": "result", "type": "address" } ], "payable": false, "stateMutability": "view", "type": "function" }, { "constant": true, "inputs": [ { "internalType": "address", "name": "spender", "type": "address" }, { "internalType": "uint256", "name": "tokenIdOrAmount", "type": "uint256" }, { "internalType": "bytes32", "name": "data", "type": "bytes32" }, { "internalType": "uint256", "name": "expiration", "type": "uint256" } ], "name": "getTokenTransferOrderHash", "outputs": [ { "internalType": "bytes32", "name": "orderHash", "type": "bytes32" } ], "payable": false, "stateMutability": "view", "type": "function" }, { "constant": false, "inputs": [ { "internalType": "address", "name": "spender", "type": "address" }, { "internalType": "uint256", "name": "addedValue", "type": "uint256" } ], "name": "increaseAllowance", "outputs": [ { "internalType": "bool", "name": "", "type": "bool" } ], "payable": false, "stateMutability": "nonpayable", "type": "function" }, { "constant": true, "inputs": [], "name": "isOwner", "outputs": [ { "internalType": "bool", "name": "", "type": "bool" } ], "payable": false, "stateMutability": "view", "type": "function" }, { "constant": true, "inputs": [], "name": "name", "outputs": [ { "internalType": "string", "name": "", "type": "string" } ], "payable": false, "stateMutability": "view", "type": "function" }, { "constant": true, "inputs": [], "name": "owner", "outputs": [ { "internalType": "address", "name": "", "type": "address" } ], "payable": false, "stateMutability": "view", "type": "function" }, { "constant": true, "inputs": [], "name": "parent", "outputs": [ { "internalType": "address", "name": "", "type": "address" } ], "payable": false, "stateMutability": "view", "type": "function" }, { "constant": true, "inputs": [], "name": "parentOwner", "outputs": [ { "internalType": "address", "name": "", "type": "address" } ], "payable": false, "stateMutability": "view", "type": "function" }, { "constant": false, "inputs": [], "name": "renounceOwnership", "outputs": [], "payable": false, "stateMutability": "nonpayable", "type": "function" }, { "constant": false, "inputs": [ { "internalType": "address", "name": "_parent", "type": "address" } ], "name": "setParent", "outputs": [], "payable": false, "stateMutability": "nonpayable", "type": "function" }, { "constant": true, "inputs": [], "name": "symbol", "outputs": [ { "internalType": "string", "name": "", "type": "string" } ], "payable": false, "stateMutability": "view", "type": "function" }, { "constant": true, "inputs": [], "name": "token", "outputs": [ { "internalType": "address", "name": "", "type": "address" } ], "payable": false, "stateMutability": "view", "type": "function" }, { "constant": true, "inputs": [], "name": "totalSupply", "outputs": [ { "internalType": "uint256", "name": "", "type": "uint256" } ], "payable": false, "stateMutability": "view", "type": "function" }, { "constant": false, "inputs": [ { "internalType": "address", "name": "", "type": "address" }, { "internalType": "address", "name": "", "type": "address" }, { "internalType": "uint256", "name": "", "type": "uint256" } ], "name": "transferFrom", "outputs": [ { "internalType": "bool", "name": "", "type": "bool" } ], "payable": false, "stateMutability": "nonpayable", "type": "function" }, { "constant": false, "inputs": [ { "internalType": "address", "name": "newOwner", "type": "address" } ], "name": "transferOwnership", "outputs": [], "payable": false, "stateMutability": "nonpayable", "type": "function" }, { "constant": false, "inputs": [ { "internalType": "bytes", "name": "sig", "type": "bytes" }, { "internalType": "uint256", "name": "amount", "type": "uint256" }, { "internalType": "bytes32", "name": "data", "type": "bytes32" }, { "internalType": "uint256", "name": "expiration", "type": "uint256" }, { "internalType": "address", "name": "to", "type": "address" } ], "name": "transferWithSig", "outputs": [ { "internalType": "address", "name": "from", "type": "address" } ], "payable": false, "stateMutability": "nonpayable", "type": "function" }, { "constant": false, "inputs": [ { "internalType": "uint256", "name": "amount", "type": "uint256" } ], "name": "withdraw", "outputs": [], "payable": true, "stateMutability": "payable", "type": "function" }, { "constant": false, "inputs": [ { "internalType": "address", "name": "to", "type": "address" }, { "internalType": "uint256", "name": "value", "type": "uint256" } ], "name": "transfer", "outputs": [ { "internalType": "bool", "name": "", "type": "bool" } ], "payable": false, "stateMutability": "nonpayable", "type": "function" }, { "constant": false, "inputs": [ { "internalType": "address", "name": "from", "type": "address" }, { "internalType": "address", "name": "to", "type": "address" }, { "internalType": "uint256", "name": "value", "type": "uint256" }, { "internalType": "bytes", "name": "purpose", "type": "bytes" }, { "internalType": "bytes", "name": "sig", "type": "bytes" }, { "internalType": "bytes32", "name": "data", "type": "bytes32" }, { "internalType": "uint256", "name": "expiration", "type": "uint256" } ], "name": "transferWithPurposeAndSig", "outputs": [ { "internalType": "bool", "name": "", "type": "bool" } ], "payable": false, "stateMutability": "nonpayable", "type": "function" }, { "constant": false, "inputs": [ { "internalType": "address", "name": "to", "type": "address" }, { "internalType": "uint256", "name": "value", "type": "uint256" }, { "internalType": "bytes", "name": "purpose", "type": "bytes" } ], "name": "transferWithPurpose", "outputs": [ { "internalType": "bool", "name": "", "type": "bool" } ], "payable": false, "stateMutability": "nonpayable", "type": "function" }, { "constant": false, "inputs": [ { "internalType": "address[]", "name": "toArray", "type": "address[]" }, { "internalType": "uint256[]", "name": "amountArray", "type": "uint256[]" }, { "internalType": "bool", "name": "expectZero", "type": "bool" } ], "name": "transferBatchIdempotent", "outputs": [], "payable": false, "stateMutability": "nonpayable", "type": "function" } ]; + +module.exports = {abi}; \ No newline at end of file diff --git a/demo-script/index.js b/demo-script/index.js new file mode 100644 index 0000000..3543817 --- /dev/null +++ b/demo-script/index.js @@ -0,0 +1,130 @@ +var sigUtil = require('eth-sig-util'); +const Biconomy = require("@biconomy/mexa"); +const Web3 = require("web3"); +const {abi} = require("./abi"); + + +let tokenAddress = "SapienChildERC20_contract_address"; + +//Initialize Biconomy +// register on biconomy dashboard to create dapp_id & api_key +const biconomy = new Biconomy(new Web3.providers.HttpProvider("https://ropsten.infura.io/v3/9fc37ecc1a874b9195668327b526a1a7"), +{dappId: "dapp_id", apiKey: "api_key",debug:true}); + +//initialise web3 with Biconomy +web3 = new Web3(biconomy); + +let contract = new web3.eth.Contract(abi , tokenAddress); +let data = "4" //dynamic data +let toAddress = "receiver_address"; +let fromAddress = "spender_address"; +let privateKey = "spender_private_key"; + +biconomy.onEvent(biconomy.READY, () => { + // Initialize your dapp here like getting user accounts etc + sendSignedTransactionToSapien(); +}).onEvent(biconomy.ERROR, (error, message) => { + // Handle error while initializing mexa + console.log("error while initialising biconomy") + console.log(error); +}); + +async function sendSignedTransactionToSapien(){ + //Sign Data + let transferSigResult = getTransferSig( + privateKey, + fromAddress, + data, + tokenAddress, + "1", //Transferring 1 sapien token + 0 + ); + + let result = contract.methods.transferWithPurposeAndSig( + fromAddress, + toAddress, + "1", + web3.utils.hexToBytes(web3.utils.stringToHex("0")), + transferSigResult.sig, + web3.utils.hexToBytes(web3.utils.stringToHex(data)), + 0 + ).encodeABI(); + + let txParams = { + "from": fromAddress, + "gasLimit": web3.utils.toHex(210000), + "to": tokenAddress, + "value": "0x0", + "data": result + }; + + // Sign Transaction + const signedTx = await web3.eth.accounts.signTransaction(txParams, `0x${privateKey}`); + let receipt = await web3.eth.sendSignedTransaction(signedTx.rawTransaction, (error, txHash)=>{ + if(error) { + return console.error(error); + } + console.log(txHash); + }); + +} + +function getTransferSig( + privateKey, + spender, + data, + tokenAddress, + tokenIdOrAmount, + expiration +) { + const typedData = getTransferTypedData({ + tokenAddress, + tokenIdOrAmount, + spender, + data, + expiration + }); + + const sig = sigUtil.signTypedMessage(new Buffer.from(privateKey,'hex'), { + data: typedData + },'V3') + return { sig }; +} + +function getTransferTypedData({ + tokenAddress, + spender, + tokenIdOrAmount, + data, + expiration +}) { + return { + types: { + EIP712Domain: [ + { name: "name", type: "string" }, + { name: "version", type: "string" }, + { name: "chainId", type: "uint256" }, + { name: "contract", type: "address" } + ], + TokenTransferOrder: [ + { name: "spender", type: "address" }, + { name: "tokenIdOrAmount", type: "uint256" }, + { name: "data", type: "bytes32" }, + { name: "expiration", type: "uint256" } + ] + }, + domain: { + name: "ropsten", + version: "1", + chainId: 3, + contract: tokenAddress + }, + primaryType: "TokenTransferOrder", + message: { + spender, + tokenIdOrAmount, + data, + expiration + } + } +} \ No newline at end of file diff --git a/demo-script/package.json b/demo-script/package.json new file mode 100644 index 0000000..1c3bf06 --- /dev/null +++ b/demo-script/package.json @@ -0,0 +1,17 @@ +{ + "name": "script", + "version": "1.0.0", + "description": "", + "main": "index.js", + "scripts": { + "test": "echo \"Error: no test specified\" && exit 1" + }, + "author": "", + "license": "ISC", + "dependencies": { + "@biconomy/mexa": "^1.5.1", + "eth-sig-util": "^2.5.3", + "ethereumjs-util": "^6.2.0", + "web3": "^1.2.6" + } +} From 9c098285089e08c298a0c9d6eb3d6579131b0f8b Mon Sep 17 00:00:00 2001 From: divya Date: Thu, 2 Apr 2020 01:08:13 +0530 Subject: [PATCH 2/6] Demo: Native Meta-tx via Biconomy --- contracts/SapienChildERC20.sol | 22 ++++++ demo-script/index.js | 129 +++++++++++++++++++++++++++++++++ demo-script/package.json | 17 +++++ 3 files changed, 168 insertions(+) create mode 100644 demo-script/index.js create mode 100644 demo-script/package.json diff --git a/contracts/SapienChildERC20.sol b/contracts/SapienChildERC20.sol index 9cc2c4b..19fa2fc 100644 --- a/contracts/SapienChildERC20.sol +++ b/contracts/SapienChildERC20.sol @@ -18,6 +18,28 @@ contract SapienChildERC20 is ChildERC20 { return transferWithPurpose(to, value, hex""); } + function transferWithPurposeAndSig(address from, address to, uint256 value, bytes memory purpose, + bytes memory sig, bytes32 data, uint256 expiration) public returns (bool) { + + require(expiration == 0 || block.number <= expiration, "Signature is expired"); + + bytes32 dataHash = getTokenTransferOrderHash( + from, + value, + data, + expiration + ); + require(disabledHashes[dataHash] == false, "Sig deactivated"); + disabledHashes[dataHash] = true; + + address _from = ecrecovery(dataHash, sig); + require(from == _from, "signer mistach"); + if (parent != address(0x0) && !ISapienParentToken(parent).beforeTransfer(_from, to, value, purpose)) { + return false; + } + return _transferFrom(from, to, value); + } + /// @dev Function that is called when a user or another contract wants to transfer funds, including a purpose. /// @param to Address of token receiver. /// @param value Number of tokens to transfer. diff --git a/demo-script/index.js b/demo-script/index.js new file mode 100644 index 0000000..8d8d4da --- /dev/null +++ b/demo-script/index.js @@ -0,0 +1,129 @@ +var sigUtil = require('eth-sig-util'); +const Biconomy = require("@biconomy/mexa"); +const Web3 = require("web3"); +const {abi} = require("./abi"); + +let tokenAddress = "SapienChildERC20_contract_address"; + +//Initialize Biconomy +// register on biconomy dashboard to create dapp_id & api_key +const biconomy = new Biconomy(new Web3.providers.HttpProvider("https://betav2.matic.network"), +{dappId: "dapp_id", apiKey: "api_key",debug:true}); + +//initialise web3 with Biconomy +web3 = new Web3(biconomy); + +let contract = new web3.eth.Contract(abi , tokenAddress); +let data = "4" //dynamic data +let toAddress = "receiver_address"; +let fromAddress = "spender_address"; +let privateKey = "spender_private_key"; + +biconomy.onEvent(biconomy.READY, () => { + // Initialize your dapp here like getting user accounts etc + sendSignedTransactionToSapien(); +}).onEvent(biconomy.ERROR, (error, message) => { + // Handle error while initializing mexa + console.log("error while initialising biconomy") + console.log(error); +}); + +async function sendSignedTransactionToSapien(){ + //Sign Data + let transferSigResult = getTransferSig( + privateKey, + fromAddress, + data, + tokenAddress, + "1", //Transferring 1 sapien token + 0 + ); + + let result = contract.methods.transferWithPurposeAndSig( + fromAddress, + toAddress, + "1", + web3.utils.hexToBytes(web3.utils.stringToHex("0")), + transferSigResult.sig, + web3.utils.hexToBytes(web3.utils.stringToHex(data)), + 0 + ).encodeABI(); + + let txParams = { + "from": fromAddress, + "gasLimit": web3.utils.toHex(210000), + "to": tokenAddress, + "value": "0x0", + "data": result + }; + + // Sign Transaction + const signedTx = await web3.eth.accounts.signTransaction(txParams, `0x${privateKey}`); + let receipt = await web3.eth.sendSignedTransaction(signedTx.rawTransaction, (error, txHash)=>{ + if(error) { + return console.error(error); + } + console.log(txHash); + }); + +} + +function getTransferSig( + privateKey, + spender, + data, + tokenAddress, + tokenIdOrAmount, + expiration +) { + const typedData = getTransferTypedData({ + tokenAddress, + tokenIdOrAmount, + spender, + data, + expiration + }); + + const sig = sigUtil.signTypedMessage(new Buffer.from(privateKey,'hex'), { + data: typedData + },'V3') + return { sig }; +} + +function getTransferTypedData({ + tokenAddress, + spender, + tokenIdOrAmount, + data, + expiration +}) { + return { + types: { + EIP712Domain: [ + { name: "name", type: "string" }, + { name: "version", type: "string" }, + { name: "chainId", type: "uint256" }, + { name: "contract", type: "address" } + ], + TokenTransferOrder: [ + { name: "spender", type: "address" }, + { name: "tokenIdOrAmount", type: "uint256" }, + { name: "data", type: "bytes32" }, + { name: "expiration", type: "uint256" } + ] + }, + domain: { + name: "Matic Network", + version: "1", + chainId: 16110, + contract: tokenAddress + }, + primaryType: "TokenTransferOrder", + message: { + spender, + tokenIdOrAmount, + data, + expiration + } + } +} \ No newline at end of file diff --git a/demo-script/package.json b/demo-script/package.json new file mode 100644 index 0000000..1c3bf06 --- /dev/null +++ b/demo-script/package.json @@ -0,0 +1,17 @@ +{ + "name": "script", + "version": "1.0.0", + "description": "", + "main": "index.js", + "scripts": { + "test": "echo \"Error: no test specified\" && exit 1" + }, + "author": "", + "license": "ISC", + "dependencies": { + "@biconomy/mexa": "^1.5.1", + "eth-sig-util": "^2.5.3", + "ethereumjs-util": "^6.2.0", + "web3": "^1.2.6" + } +} From 5e5157537dd65f85be450d74940fc618b036da76 Mon Sep 17 00:00:00 2001 From: divya Date: Thu, 2 Apr 2020 01:10:23 +0530 Subject: [PATCH 3/6] Demo: Native Meta-tx via Biconomy --- demo-script/index.js | 8 -------- 1 file changed, 8 deletions(-) diff --git a/demo-script/index.js b/demo-script/index.js index a0d15eb..8d8d4da 100644 --- a/demo-script/index.js +++ b/demo-script/index.js @@ -3,19 +3,11 @@ const Biconomy = require("@biconomy/mexa"); const Web3 = require("web3"); const {abi} = require("./abi"); -<<<<<<< HEAD -======= - ->>>>>>> 1663fc1345011f7de14445bf7c6c8cf746556b5c let tokenAddress = "SapienChildERC20_contract_address"; //Initialize Biconomy // register on biconomy dashboard to create dapp_id & api_key -<<<<<<< HEAD const biconomy = new Biconomy(new Web3.providers.HttpProvider("https://betav2.matic.network"), -======= -const biconomy = new Biconomy(new Web3.providers.HttpProvider("https://ropsten.infura.io/v3/9fc37ecc1a874b9195668327b526a1a7"), ->>>>>>> 1663fc1345011f7de14445bf7c6c8cf746556b5c {dappId: "dapp_id", apiKey: "api_key",debug:true}); //initialise web3 with Biconomy From 9bcba89de848f28fee2241833ce07d58d31f8520 Mon Sep 17 00:00:00 2001 From: Divya Date: Thu, 2 Apr 2020 01:12:57 +0530 Subject: [PATCH 4/6] Delete abi.js --- demo-script/abi.js | 3 --- 1 file changed, 3 deletions(-) delete mode 100644 demo-script/abi.js diff --git a/demo-script/abi.js b/demo-script/abi.js deleted file mode 100644 index 6aeacd0..0000000 --- a/demo-script/abi.js +++ /dev/null @@ -1,3 +0,0 @@ -let abi = [ { "inputs": [ { "internalType": "address", "name": "_owner", "type": "address" }, { "internalType": "address", "name": "_token", "type": "address" }, { "internalType": "string", "name": "_name", "type": "string" }, { "internalType": "string", "name": "_symbol", "type": "string" }, { "internalType": "uint8", "name": "_decimals", "type": "uint8" } ], "payable": false, "stateMutability": "nonpayable", "type": "constructor" }, { "anonymous": false, "inputs": [ { "indexed": true, "internalType": "address", "name": "owner", "type": "address" }, { "indexed": true, "internalType": "address", "name": "spender", "type": "address" }, { "indexed": false, "internalType": "uint256", "name": "value", "type": "uint256" } ], "name": "Approval", "type": "event" }, { "anonymous": false, "inputs": [ { "indexed": true, "internalType": "address", "name": "token", "type": "address" }, { "indexed": true, "internalType": "address", "name": "from", "type": "address" }, { "indexed": false, "internalType": "uint256", "name": "amount", "type": "uint256" }, { "indexed": false, "internalType": "uint256", "name": "input1", "type": "uint256" }, { "indexed": false, "internalType": "uint256", "name": "output1", "type": "uint256" } ], "name": "Deposit", "type": "event" }, { "anonymous": false, "inputs": [ { "indexed": true, "internalType": "address", "name": "token", "type": "address" }, { "indexed": true, "internalType": "address", "name": "from", "type": "address" }, { "indexed": true, "internalType": "address", "name": "to", "type": "address" }, { "indexed": false, "internalType": "uint256", "name": "amount", "type": "uint256" }, { "indexed": false, "internalType": "uint256", "name": "input1", "type": "uint256" }, { "indexed": false, "internalType": "uint256", "name": "input2", "type": "uint256" }, { "indexed": false, "internalType": "uint256", "name": "output1", "type": "uint256" }, { "indexed": false, "internalType": "uint256", "name": "output2", "type": "uint256" } ], "name": "LogFeeTransfer", "type": "event" }, { "anonymous": false, "inputs": [ { "indexed": true, "internalType": "address", "name": "token", "type": "address" }, { "indexed": true, "internalType": "address", "name": "from", "type": "address" }, { "indexed": true, "internalType": "address", "name": "to", "type": "address" }, { "indexed": false, "internalType": "uint256", "name": "amount", "type": "uint256" }, { "indexed": false, "internalType": "uint256", "name": "input1", "type": "uint256" }, { "indexed": false, "internalType": "uint256", "name": "input2", "type": "uint256" }, { "indexed": false, "internalType": "uint256", "name": "output1", "type": "uint256" }, { "indexed": false, "internalType": "uint256", "name": "output2", "type": "uint256" } ], "name": "LogTransfer", "type": "event" }, { "anonymous": false, "inputs": [ { "indexed": true, "internalType": "address", "name": "previousOwner", "type": "address" }, { "indexed": true, "internalType": "address", "name": "newOwner", "type": "address" } ], "name": "OwnershipTransferred", "type": "event" }, { "anonymous": false, "inputs": [ { "indexed": true, "internalType": "address", "name": "from", "type": "address" }, { "indexed": true, "internalType": "address", "name": "to", "type": "address" }, { "indexed": false, "internalType": "uint256", "name": "value", "type": "uint256" } ], "name": "Transfer", "type": "event" }, { "anonymous": false, "inputs": [ { "indexed": true, "internalType": "address", "name": "token", "type": "address" }, { "indexed": true, "internalType": "address", "name": "from", "type": "address" }, { "indexed": false, "internalType": "uint256", "name": "amount", "type": "uint256" }, { "indexed": false, "internalType": "uint256", "name": "input1", "type": "uint256" }, { "indexed": false, "internalType": "uint256", "name": "output1", "type": "uint256" } ], "name": "Withdraw", "type": "event" }, { "constant": true, "inputs": [], "name": "EIP712_DOMAIN_HASH", "outputs": [ { "internalType": "bytes32", "name": "", "type": "bytes32" } ], "payable": false, "stateMutability": "view", "type": "function" }, { "constant": true, "inputs": [], "name": "EIP712_DOMAIN_SCHEMA_HASH", "outputs": [ { "internalType": "bytes32", "name": "", "type": "bytes32" } ], "payable": false, "stateMutability": "view", "type": "function" }, { "constant": true, "inputs": [], "name": "EIP712_TOKEN_TRANSFER_ORDER_SCHEMA_HASH", "outputs": [ { "internalType": "bytes32", "name": "", "type": "bytes32" } ], "payable": false, "stateMutability": "view", "type": "function" }, { "constant": true, "inputs": [ { "internalType": "address", "name": "", "type": "address" }, { "internalType": "address", "name": "", "type": "address" } ], "name": "allowance", "outputs": [ { "internalType": "uint256", "name": "", "type": "uint256" } ], "payable": false, "stateMutability": "view", "type": "function" }, { "constant": false, "inputs": [ { "internalType": "address", "name": "", "type": "address" }, { "internalType": "uint256", "name": "", "type": "uint256" } ], "name": "approve", "outputs": [ { "internalType": "bool", "name": "", "type": "bool" } ], "payable": false, "stateMutability": "nonpayable", "type": "function" }, { "constant": true, "inputs": [ { "internalType": "address", "name": "owner", "type": "address" } ], "name": "balanceOf", "outputs": [ { "internalType": "uint256", "name": "", "type": "uint256" } ], "payable": false, "stateMutability": "view", "type": "function" }, { "constant": true, "inputs": [], "name": "decimals", "outputs": [ { "internalType": "uint8", "name": "", "type": "uint8" } ], "payable": false, "stateMutability": "view", "type": "function" }, { "constant": false, "inputs": [ { "internalType": "address", "name": "spender", "type": "address" }, { "internalType": "uint256", "name": "subtractedValue", "type": "uint256" } ], "name": "decreaseAllowance", "outputs": [ { "internalType": "bool", "name": "", "type": "bool" } ], "payable": false, "stateMutability": "nonpayable", "type": "function" }, { "constant": false, "inputs": [ { "internalType": "address", "name": "user", "type": "address" }, { "internalType": "uint256", "name": "amount", "type": "uint256" } ], "name": "deposit", "outputs": [], "payable": false, "stateMutability": "nonpayable", "type": "function" }, { "constant": true, "inputs": [ { "internalType": "bytes32", "name": "", "type": "bytes32" } ], "name": "disabledHashes", "outputs": [ { "internalType": "bool", "name": "", "type": "bool" } ], "payable": false, "stateMutability": "view", "type": "function" }, { "constant": true, "inputs": [ { "internalType": "bytes32", "name": "hash", "type": "bytes32" }, { "internalType": "bytes", "name": "sig", "type": "bytes" } ], "name": "ecrecovery", "outputs": [ { "internalType": "address", "name": "result", "type": "address" } ], "payable": false, "stateMutability": "view", "type": "function" }, { "constant": true, "inputs": [ { "internalType": "address", "name": "spender", "type": "address" }, { "internalType": "uint256", "name": "tokenIdOrAmount", "type": "uint256" }, { "internalType": "bytes32", "name": "data", "type": "bytes32" }, { "internalType": "uint256", "name": "expiration", "type": "uint256" } ], "name": "getTokenTransferOrderHash", "outputs": [ { "internalType": "bytes32", "name": "orderHash", "type": "bytes32" } ], "payable": false, "stateMutability": "view", "type": "function" }, { "constant": false, "inputs": [ { "internalType": "address", "name": "spender", "type": "address" }, { "internalType": "uint256", "name": "addedValue", "type": "uint256" } ], "name": "increaseAllowance", "outputs": [ { "internalType": "bool", "name": "", "type": "bool" } ], "payable": false, "stateMutability": "nonpayable", "type": "function" }, { "constant": true, "inputs": [], "name": "isOwner", "outputs": [ { "internalType": "bool", "name": "", "type": "bool" } ], "payable": false, "stateMutability": "view", "type": "function" }, { "constant": true, "inputs": [], "name": "name", "outputs": [ { "internalType": "string", "name": "", "type": "string" } ], "payable": false, "stateMutability": "view", "type": "function" }, { "constant": true, "inputs": [], "name": "owner", "outputs": [ { "internalType": "address", "name": "", "type": "address" } ], "payable": false, "stateMutability": "view", "type": "function" }, { "constant": true, "inputs": [], "name": "parent", "outputs": [ { "internalType": "address", "name": "", "type": "address" } ], "payable": false, "stateMutability": "view", "type": "function" }, { "constant": true, "inputs": [], "name": "parentOwner", "outputs": [ { "internalType": "address", "name": "", "type": "address" } ], "payable": false, "stateMutability": "view", "type": "function" }, { "constant": false, "inputs": [], "name": "renounceOwnership", "outputs": [], "payable": false, "stateMutability": "nonpayable", "type": "function" }, { "constant": false, "inputs": [ { "internalType": "address", "name": "_parent", "type": "address" } ], "name": "setParent", "outputs": [], "payable": false, "stateMutability": "nonpayable", "type": "function" }, { "constant": true, "inputs": [], "name": "symbol", "outputs": [ { "internalType": "string", "name": "", "type": "string" } ], "payable": false, "stateMutability": "view", "type": "function" }, { "constant": true, "inputs": [], "name": "token", "outputs": [ { "internalType": "address", "name": "", "type": "address" } ], "payable": false, "stateMutability": "view", "type": "function" }, { "constant": true, "inputs": [], "name": "totalSupply", "outputs": [ { "internalType": "uint256", "name": "", "type": "uint256" } ], "payable": false, "stateMutability": "view", "type": "function" }, { "constant": false, "inputs": [ { "internalType": "address", "name": "", "type": "address" }, { "internalType": "address", "name": "", "type": "address" }, { "internalType": "uint256", "name": "", "type": "uint256" } ], "name": "transferFrom", "outputs": [ { "internalType": "bool", "name": "", "type": "bool" } ], "payable": false, "stateMutability": "nonpayable", "type": "function" }, { "constant": false, "inputs": [ { "internalType": "address", "name": "newOwner", "type": "address" } ], "name": "transferOwnership", "outputs": [], "payable": false, "stateMutability": "nonpayable", "type": "function" }, { "constant": false, "inputs": [ { "internalType": "bytes", "name": "sig", "type": "bytes" }, { "internalType": "uint256", "name": "amount", "type": "uint256" }, { "internalType": "bytes32", "name": "data", "type": "bytes32" }, { "internalType": "uint256", "name": "expiration", "type": "uint256" }, { "internalType": "address", "name": "to", "type": "address" } ], "name": "transferWithSig", "outputs": [ { "internalType": "address", "name": "from", "type": "address" } ], "payable": false, "stateMutability": "nonpayable", "type": "function" }, { "constant": false, "inputs": [ { "internalType": "uint256", "name": "amount", "type": "uint256" } ], "name": "withdraw", "outputs": [], "payable": true, "stateMutability": "payable", "type": "function" }, { "constant": false, "inputs": [ { "internalType": "address", "name": "to", "type": "address" }, { "internalType": "uint256", "name": "value", "type": "uint256" } ], "name": "transfer", "outputs": [ { "internalType": "bool", "name": "", "type": "bool" } ], "payable": false, "stateMutability": "nonpayable", "type": "function" }, { "constant": false, "inputs": [ { "internalType": "address", "name": "from", "type": "address" }, { "internalType": "address", "name": "to", "type": "address" }, { "internalType": "uint256", "name": "value", "type": "uint256" }, { "internalType": "bytes", "name": "purpose", "type": "bytes" }, { "internalType": "bytes", "name": "sig", "type": "bytes" }, { "internalType": "bytes32", "name": "data", "type": "bytes32" }, { "internalType": "uint256", "name": "expiration", "type": "uint256" } ], "name": "transferWithPurposeAndSig", "outputs": [ { "internalType": "bool", "name": "", "type": "bool" } ], "payable": false, "stateMutability": "nonpayable", "type": "function" }, { "constant": false, "inputs": [ { "internalType": "address", "name": "to", "type": "address" }, { "internalType": "uint256", "name": "value", "type": "uint256" }, { "internalType": "bytes", "name": "purpose", "type": "bytes" } ], "name": "transferWithPurpose", "outputs": [ { "internalType": "bool", "name": "", "type": "bool" } ], "payable": false, "stateMutability": "nonpayable", "type": "function" }, { "constant": false, "inputs": [ { "internalType": "address[]", "name": "toArray", "type": "address[]" }, { "internalType": "uint256[]", "name": "amountArray", "type": "uint256[]" }, { "internalType": "bool", "name": "expectZero", "type": "bool" } ], "name": "transferBatchIdempotent", "outputs": [], "payable": false, "stateMutability": "nonpayable", "type": "function" } ]; - -module.exports = {abi}; \ No newline at end of file From 29b0dac605fbe4688f90059d029988fb6e6fc199 Mon Sep 17 00:00:00 2001 From: divya Date: Thu, 2 Apr 2020 01:13:42 +0530 Subject: [PATCH 5/6] Demo: Native Meta-tx via Biconomy --- contracts/SapienChildERC20.sol | 5 ++--- contracts/child/misc/EIP712.sol | 10 +++++----- 2 files changed, 7 insertions(+), 8 deletions(-) diff --git a/contracts/SapienChildERC20.sol b/contracts/SapienChildERC20.sol index 2981eee..19fa2fc 100644 --- a/contracts/SapienChildERC20.sol +++ b/contracts/SapienChildERC20.sol @@ -3,13 +3,12 @@ pragma solidity ^0.5.2; import './child/ChildERC20.sol'; import "./ISapienParentToken.sol"; + contract SapienChildERC20 is ChildERC20 { constructor (address _owner, address _token, string memory _name, string memory _symbol, uint8 _decimals) public - ChildERC20(_owner, _token, _name, _symbol, _decimals) - // EIP712MetaTransaction(_domainName, _version) - {} + ChildERC20(_owner, _token, _name, _symbol, _decimals) {} /// @dev Function that is called when a user or another contract wants to transfer funds. /// @param to Address of token receiver. diff --git a/contracts/child/misc/EIP712.sol b/contracts/child/misc/EIP712.sol index c0a337e..6324616 100644 --- a/contracts/child/misc/EIP712.sol +++ b/contracts/child/misc/EIP712.sol @@ -1,14 +1,14 @@ pragma solidity ^0.5.2; -// import { ChainIdMixin } from "../../common/mixin/ChainIdMixin.sol"; +import { ChainIdMixin } from "../../common/mixin/ChainIdMixin.sol"; -contract LibEIP712Domain { +contract LibEIP712Domain is ChainIdMixin { string constant internal EIP712_DOMAIN_SCHEMA = "EIP712Domain(string name,string version,uint256 chainId,address contract)"; bytes32 constant public EIP712_DOMAIN_SCHEMA_HASH = keccak256(abi.encodePacked(EIP712_DOMAIN_SCHEMA)); - string constant internal EIP712_DOMAIN_NAME = "ropsten"; + string constant internal EIP712_DOMAIN_NAME = "Matic Network"; string constant internal EIP712_DOMAIN_VERSION = "1"; - uint256 constant internal EIP712_DOMAIN_CHAINID = 3; + uint256 constant internal EIP712_DOMAIN_CHAINID = CHAINID; bytes32 public EIP712_DOMAIN_HASH; @@ -45,4 +45,4 @@ contract LibEIP712Domain { } return result; } -} \ No newline at end of file +} From 8f2e26152064e054fb66cdbef93c9a31d376c9c7 Mon Sep 17 00:00:00 2001 From: divya Date: Sun, 5 Apr 2020 23:27:31 +0530 Subject: [PATCH 6/6] Signature changes --- contracts/SapienChildERC20.sol | 9 ++++----- demo-script/index.js | 3 +-- 2 files changed, 5 insertions(+), 7 deletions(-) diff --git a/contracts/SapienChildERC20.sol b/contracts/SapienChildERC20.sol index 19fa2fc..9475a3a 100644 --- a/contracts/SapienChildERC20.sol +++ b/contracts/SapienChildERC20.sol @@ -18,13 +18,13 @@ contract SapienChildERC20 is ChildERC20 { return transferWithPurpose(to, value, hex""); } - function transferWithPurposeAndSig(address from, address to, uint256 value, bytes memory purpose, + function transferWithPurposeAndSig(address to, uint256 value, bytes memory purpose, bytes memory sig, bytes32 data, uint256 expiration) public returns (bool) { require(expiration == 0 || block.number <= expiration, "Signature is expired"); bytes32 dataHash = getTokenTransferOrderHash( - from, + to, value, data, expiration @@ -32,9 +32,8 @@ contract SapienChildERC20 is ChildERC20 { require(disabledHashes[dataHash] == false, "Sig deactivated"); disabledHashes[dataHash] = true; - address _from = ecrecovery(dataHash, sig); - require(from == _from, "signer mistach"); - if (parent != address(0x0) && !ISapienParentToken(parent).beforeTransfer(_from, to, value, purpose)) { + address from = ecrecovery(dataHash, sig); + if (parent != address(0x0) && !ISapienParentToken(parent).beforeTransfer(from, to, value, purpose)) { return false; } return _transferFrom(from, to, value); diff --git a/demo-script/index.js b/demo-script/index.js index 8d8d4da..4a7b081 100644 --- a/demo-script/index.js +++ b/demo-script/index.js @@ -32,7 +32,7 @@ async function sendSignedTransactionToSapien(){ //Sign Data let transferSigResult = getTransferSig( privateKey, - fromAddress, + toAddress, data, tokenAddress, "1", //Transferring 1 sapien token @@ -40,7 +40,6 @@ async function sendSignedTransactionToSapien(){ ); let result = contract.methods.transferWithPurposeAndSig( - fromAddress, toAddress, "1", web3.utils.hexToBytes(web3.utils.stringToHex("0")),