Fix wrong transaction input for contract deployments (#4052)

* Fix mutable options in Contract API

* Add Swarm hash and meta data from Solidity

* Updates tests for contract deployment

* Add test for deploy without constructor Params
This commit is contained in:
Nicolas Gotchac 2017-01-06 10:39:18 +01:00 committed by Gav Wood
parent 1b93d79a90
commit 9ab9ff2381
3 changed files with 119 additions and 14 deletions

View File

@ -218,14 +218,19 @@ export default class Contract {
} }
_encodeOptions (func, options, values) { _encodeOptions (func, options, values) {
options.data = this.getCallData(func, options, values); const data = this.getCallData(func, options, values);
return options;
return {
...options,
data
};
} }
_addOptionsTo (options = {}) { _addOptionsTo (options = {}) {
return Object.assign({ return {
to: this._address to: this._address,
}, options); ...options
};
} }
_bindFunction = (func) => { _bindFunction = (func) => {

View File

@ -31,6 +31,7 @@ const eth = new Api(transport);
describe('api/contract/Contract', () => { describe('api/contract/Contract', () => {
const ADDR = '0x0123456789'; const ADDR = '0x0123456789';
const ABI = [ const ABI = [
{ {
type: 'function', name: 'test', type: 'function', name: 'test',
@ -41,12 +42,42 @@ describe('api/contract/Contract', () => {
type: 'function', name: 'test2', type: 'function', name: 'test2',
outputs: [{ type: 'uint' }, { type: 'uint' }] outputs: [{ type: 'uint' }, { type: 'uint' }]
}, },
{ type: 'constructor' }, {
type: 'constructor',
inputs: [{ name: 'boolin', type: 'bool' }, { name: 'stringin', type: 'string' }]
},
{ type: 'event', name: 'baz' }, { type: 'event', name: 'baz' },
{ type: 'event', name: 'foo' } { type: 'event', name: 'foo' }
]; ];
const VALUES = [true, 'jacogr'];
const ENCODED = '0x023562050000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000066a61636f67720000000000000000000000000000000000000000000000000000'; const ABI_NO_PARAMS = [
{
type: 'function', name: 'test',
inputs: [{ name: 'boolin', type: 'bool' }, { name: 'stringin', type: 'string' }],
outputs: [{ type: 'uint' }]
},
{
type: 'function', name: 'test2',
outputs: [{ type: 'uint' }, { type: 'uint' }]
},
{
type: 'constructor'
},
{ type: 'event', name: 'baz' },
{ type: 'event', name: 'foo' }
];
const VALUES = [ true, 'jacogr' ];
const CALLDATA = `
0000000000000000000000000000000000000000000000000000000000000001
0000000000000000000000000000000000000000000000000000000000000040
0000000000000000000000000000000000000000000000000000000000000006
6a61636f67720000000000000000000000000000000000000000000000000000
`.replace(/\s/g, '');
const SIGNATURE = '02356205';
const ENCODED = `0x${SIGNATURE}${CALLDATA}`;
const RETURN1 = '0000000000000000000000000000000000000000000000000000000000123456'; const RETURN1 = '0000000000000000000000000000000000000000000000000000000000123456';
const RETURN2 = '0000000000000000000000000000000000000000000000000000000000456789'; const RETURN2 = '0000000000000000000000000000000000000000000000000000000000456789';
let scope; let scope;
@ -230,6 +261,33 @@ describe('api/contract/Contract', () => {
}); });
}); });
describe('deploy without parameters', () => {
const contract = new Contract(eth, ABI_NO_PARAMS);
const CODE = '0x123';
const ADDRESS = '0xD337e80eEdBdf86eDBba021797d7e4e00Bb78351';
const RECEIPT_DONE = { contractAddress: ADDRESS.toLowerCase(), gasUsed: 50, blockNumber: 2500 };
let scope;
describe('success', () => {
before(() => {
scope = mockHttp([
{ method: 'eth_estimateGas', reply: { result: 1000 } },
{ method: 'parity_postTransaction', reply: { result: '0x678' } },
{ method: 'parity_checkRequest', reply: { result: '0x890' } },
{ method: 'eth_getTransactionReceipt', reply: { result: RECEIPT_DONE } },
{ method: 'eth_getCode', reply: { result: CODE } }
]);
return contract.deploy({ data: CODE }, []);
});
it('passes the options through to postTransaction (incl. gas calculation)', () => {
expect(scope.body.parity_postTransaction.params[0].data).to.equal(CODE);
});
});
});
describe('deploy', () => { describe('deploy', () => {
const contract = new Contract(eth, ABI); const contract = new Contract(eth, ABI);
const ADDRESS = '0xD337e80eEdBdf86eDBba021797d7e4e00Bb78351'; const ADDRESS = '0xD337e80eEdBdf86eDBba021797d7e4e00Bb78351';
@ -252,7 +310,7 @@ describe('api/contract/Contract', () => {
{ method: 'eth_getCode', reply: { result: '0x456' } } { method: 'eth_getCode', reply: { result: '0x456' } }
]); ]);
return contract.deploy({ data: '0x123' }, []); return contract.deploy({ data: '0x123' }, VALUES);
}); });
it('calls estimateGas, postTransaction, checkRequest, getTransactionReceipt & getCode in order', () => { it('calls estimateGas, postTransaction, checkRequest, getTransactionReceipt & getCode in order', () => {
@ -261,7 +319,7 @@ describe('api/contract/Contract', () => {
it('passes the options through to postTransaction (incl. gas calculation)', () => { it('passes the options through to postTransaction (incl. gas calculation)', () => {
expect(scope.body.parity_postTransaction.params).to.deep.equal([ expect(scope.body.parity_postTransaction.params).to.deep.equal([
{ data: '0x123', gas: '0x4b0' } { data: `0x123${CALLDATA}`, gas: '0x4b0' }
]); ]);
}); });
@ -280,7 +338,7 @@ describe('api/contract/Contract', () => {
]); ]);
return contract return contract
.deploy({ data: '0x123' }, []) .deploy({ data: '0x123' }, VALUES)
.catch((error) => { .catch((error) => {
expect(error.message).to.match(/not deployed, gasUsed/); expect(error.message).to.match(/not deployed, gasUsed/);
}); });
@ -296,7 +354,7 @@ describe('api/contract/Contract', () => {
]); ]);
return contract return contract
.deploy({ data: '0x123' }, []) .deploy({ data: '0x123' }, VALUES)
.catch((error) => { .catch((error) => {
expect(error.message).to.match(/not deployed, getCode/); expect(error.message).to.match(/not deployed, getCode/);
}); });

View File

@ -458,23 +458,65 @@ class WriteContract extends Component {
const { bytecode } = contract; const { bytecode } = contract;
const abi = contract.interface; const abi = contract.interface;
const metadata = contract.metadata
? (
<Input
allowCopy
label='Metadata'
readOnly
value={ contract.metadata }
/>
)
: null;
return ( return (
<div> <div>
<Input <Input
allowCopy
label='ABI Interface'
readOnly readOnly
value={ abi } value={ abi }
label='ABI Interface'
/> />
<Input <Input
allowCopy
label='Bytecode'
readOnly readOnly
value={ `0x${bytecode}` } value={ `0x${bytecode}` }
label='Bytecode'
/> />
{ metadata }
{ this.renderSwarmHash(contract) }
</div> </div>
); );
} }
renderSwarmHash (contract) {
if (!contract || !contract.metadata) {
return null;
}
const { bytecode } = contract;
// @see https://solidity.readthedocs.io/en/develop/miscellaneous.html#encoding-of-the-metadata-hash-in-the-bytecode
const hashRegex = /a165627a7a72305820([a-f0-9]{64})0029$/;
if (!hashRegex.test(bytecode)) {
return null;
}
const hash = hashRegex.exec(bytecode)[1];
return (
<Input
allowCopy
label='Swarm Metadata Hash'
readOnly
value={ `${hash}` }
/>
);
}
renderErrors () { renderErrors () {
const { annotations } = this.store; const { annotations } = this.store;