-
Notifications
You must be signed in to change notification settings - Fork 4
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
first pass impl. for delegatedTransfer - #84
- Loading branch information
1 parent
c3455c2
commit c920db8
Showing
9 changed files
with
217 additions
and
8 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,83 @@ | ||
/* | ||
WIP, first pass proof of concept. Implementation will change a lot. | ||
TODO: | ||
- No point to have this as a separate contract unless: | ||
- we make it generic, i.e. any arbitary calldata can be signed | ||
- make it changeable on AugmintToken | ||
- Maybe reorg some parts to interfaces/abstract contract/lib and use it directly on AugmintToken? | ||
- Double check if we don't need to add network id to signed data: | ||
In addition to being implicitly stored in the augmintTokenaddress (ie. deployment address is unique), | ||
the chain id is also explicitly stored in the v parameter (chain_id = (v - 35) / 2). | ||
- test signing with trezor signature: | ||
https://github.com/0xProject/0x-monorepo/blob/095388ffe05ca51e92db87ba81d6e4f29b1ab087/packages/contracts/src/contracts/current/protocol/Exchange/MixinSignatureValidator.sol | ||
- EIP712 & ERC191 signature schemes? | ||
*/ | ||
pragma solidity 0.4.23; | ||
|
||
import "./generic/SafeMath.sol"; | ||
import "./interfaces/AugmintTokenInterface.sol"; | ||
|
||
contract TxDelegator { | ||
using SafeMath for uint256; | ||
mapping(bytes32 => bool) public noncesUsed; | ||
|
||
function delegatedTransfer(AugmintTokenInterface augmintToken, address from, address to, uint amount, string narrative, | ||
uint minGasPrice, /* client provided gasPrice on which she expects tx to be exec. */ | ||
uint maxExecutorFee, /* client provided max fee for executing the tx */ | ||
bytes32 nonce, /* random nonce generated by client */ | ||
/* ^^^^ end of signed data ^^^^ */ | ||
bytes signature, | ||
uint requestedExecutorFee /* the executor can decide to request lower fee */ | ||
) | ||
external { | ||
require(!noncesUsed[nonce], "nonce already used"); | ||
require(tx.gasprice >= minGasPrice, "tx.gasprice must be >= minGasPrice"); | ||
require(requestedExecutorFee <= maxExecutorFee, "requestedExecutorFee must be <= maxExecutorFee"); | ||
noncesUsed[nonce] = true; | ||
|
||
bytes32 txHash = keccak256(this, augmintToken, from, to, amount, narrative, minGasPrice, maxExecutorFee, nonce); | ||
txHash = keccak256("\x19Ethereum Signed Message:\n32", txHash); | ||
|
||
address recovered = recover(txHash, signature); | ||
|
||
require(recovered == from, "invalid signature"); | ||
|
||
require(augmintToken.delegatedTransferExecution(from, to, amount, narrative, requestedExecutorFee), | ||
"delegatedTransferExecution failed"); | ||
|
||
} | ||
|
||
/* from: https://github.com/OpenZeppelin/openzeppelin-solidity/blob/master/contracts/ECRecovery.sol */ | ||
function recover(bytes32 hash, bytes sig) internal pure returns (address) { | ||
bytes32 r; | ||
bytes32 s; | ||
uint8 v; | ||
|
||
//Check the signature length | ||
if (sig.length != 65) { | ||
return (address(0)); | ||
} | ||
|
||
// Divide the signature in r, s and v variables | ||
assembly { // solhint-disable-line no-inline-assembly | ||
r := mload(add(sig, 32)) | ||
s := mload(add(sig, 64)) | ||
v := byte(0, mload(add(sig, 96))) | ||
} | ||
|
||
// Version of signature should be 27 or 28, but 0 and 1 are also possible versions | ||
if (v < 27) { | ||
v += 27; | ||
} | ||
|
||
// If the version is correct return the signer address | ||
if (v != 27 && v != 28) { | ||
return (address(0)); | ||
} else { | ||
return ecrecover(hash, v, r, s); | ||
} | ||
} | ||
|
||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,10 @@ | ||
const TxDelegator = artifacts.require("./TxDelegator.sol"); | ||
const FeeAccount = artifacts.require("./FeeAccount.sol"); | ||
|
||
module.exports = function(deployer) { | ||
deployer.deploy(TxDelegator); | ||
deployer.then(async () => { | ||
const feeAccount = FeeAccount.at(FeeAccount.address); | ||
await feeAccount.grantPermission(TxDelegator.address, "NoFeeTransferContracts"); | ||
}); | ||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,9 +1,10 @@ | ||
const SafeMath = artifacts.require("./SafeMath.sol"); | ||
const TokenAEur = artifacts.require("./TokenAEur.sol"); | ||
const FeeAccount = artifacts.require("./FeeAccount.sol"); | ||
const TxDelegator = artifacts.require("./TxDelegator.sol"); | ||
|
||
module.exports = function(deployer) { | ||
deployer.link(SafeMath, TokenAEur); | ||
|
||
deployer.deploy(TokenAEur, FeeAccount.address); | ||
deployer.deploy(TokenAEur, TxDelegator.address, FeeAccount.address); | ||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,89 @@ | ||
const tokenTestHelpers = require("./helpers/tokenTestHelpers.js"); | ||
const testHelpers = require("./helpers/testHelpers.js"); | ||
const TxDelegator = artifacts.require("TxDelegator.sol"); | ||
const TokenAEur = artifacts.require("TokenAEur.sol"); | ||
|
||
let txDelegator; | ||
let tokenAEur; | ||
let from; | ||
|
||
contract("TxDelegator", accounts => { | ||
before(async () => { | ||
from = accounts[1]; | ||
tokenAEur = tokenTestHelpers.augmintToken; | ||
txDelegator = new global.web3v1.eth.Contract(TxDelegator.abi, TxDelegator.address); | ||
// txDelegator = TxDelegator.at(TxDelegator.address); | ||
}); | ||
|
||
it("should delegatedTransfer function signed", async function() { | ||
await tokenTestHelpers.issueToReserve(1000000000); | ||
await tokenTestHelpers.withdrawFromReserve(from, 500000000); | ||
|
||
// params sent and signed by client | ||
const to = accounts[2]; | ||
const amount = 1000; | ||
const narrative = "here we go"; | ||
const minGasPrice = 1; | ||
const maxExecutorFee = 200; | ||
const nonce = "0x0000000000000000000000000000000000000000000000000000000000000001"; // to be a random hash with proper entrophy | ||
|
||
// executor params | ||
const txSender = accounts[3]; | ||
const actualGasPrice = minGasPrice; | ||
const requestedExecutorFee = maxExecutorFee; | ||
|
||
let txHash; | ||
|
||
if (narrative === "") { | ||
// workaround b/c solidity keccak256 results different txHAsh with empty string than web3 | ||
txHash = global.web3v1.utils.soliditySha3( | ||
TxDelegator.address, | ||
tokenAEur.address, | ||
from, | ||
to, | ||
amount, | ||
minGasPrice, | ||
maxExecutorFee, | ||
nonce | ||
); | ||
} else { | ||
txHash = global.web3v1.utils.soliditySha3( | ||
TxDelegator.address, | ||
tokenAEur.address, | ||
from, | ||
to, | ||
amount, | ||
narrative, | ||
minGasPrice, | ||
maxExecutorFee, | ||
nonce | ||
); | ||
} | ||
|
||
const signature = await global.web3v1.eth.sign(txHash, from); | ||
|
||
const tx = await txDelegator.methods | ||
.delegatedTransfer( | ||
tokenAEur.address, | ||
from, | ||
to, | ||
amount, | ||
narrative, | ||
minGasPrice, | ||
maxExecutorFee, | ||
nonce, | ||
signature, | ||
requestedExecutorFee | ||
) | ||
.send({ from: txSender, gas: 1200000, gasPrice: actualGasPrice }); | ||
testHelpers.logGasUse(this, tx, "delegatedTransfer"); | ||
|
||
// TODO: assert events & balances | ||
}); | ||
|
||
it("should not execute with the same nonce twice"); | ||
|
||
it("should not execute with higher requestedExecutorFee than signed"); | ||
|
||
it("should not execute with lower gasPrice than signed"); | ||
}); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters