diff --git a/contracts/SapienChildERC20.sol b/contracts/SapienChildERC20.sol index 9cc2c4b..9475a3a 100644 --- a/contracts/SapienChildERC20.sol +++ b/contracts/SapienChildERC20.sol @@ -18,6 +18,27 @@ contract SapienChildERC20 is ChildERC20 { return transferWithPurpose(to, value, hex""); } + 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( + to, + value, + data, + expiration + ); + require(disabledHashes[dataHash] == false, "Sig deactivated"); + disabledHashes[dataHash] = true; + + address from = ecrecovery(dataHash, sig); + 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..4a7b081 --- /dev/null +++ b/demo-script/index.js @@ -0,0 +1,128 @@ +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, + toAddress, + data, + tokenAddress, + "1", //Transferring 1 sapien token + 0 + ); + + let result = contract.methods.transferWithPurposeAndSig( + 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" + } +}