contract.js 7.22 KB
Newer Older
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
/*
    This file is part of ethereum.js.

    ethereum.js is free software: you can redistribute it and/or modify
    it under the terms of the GNU Lesser General Public License as published by
    the Free Software Foundation, either version 3 of the License, or
    (at your option) any later version.

    ethereum.js is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU Lesser General Public License for more details.

    You should have received a copy of the GNU Lesser General Public License
    along with ethereum.js.  If not, see <http://www.gnu.org/licenses/>.
*/
/** @file contract.js
 * @authors:
 *   Marek Kotewicz <marek@ethdev.com>
 * @date 2014
 */

Marek Kotewicz's avatar
Marek Kotewicz committed
23
var web3 = require('./web3'); 
24
var abi = require('./abi');
25
var utils = require('./utils');
Marek Kotewicz's avatar
Marek Kotewicz committed
26
var eventImpl = require('./event');
27

28 29 30 31 32 33 34 35 36
var exportNatspecGlobals = function (vars) {
    // it's used byt natspec.js
    // TODO: figure out better way to solve this
    web3._currentContractAbi = vars.abi;
    web3._currentContractAddress = vars.address;
    web3._currentContractMethodName = vars.method;
    web3._currentContractMethodParams = vars.params;
};

Marek Kotewicz's avatar
Marek Kotewicz committed
37 38 39 40 41 42
var addFunctionRelatedPropertiesToContract = function (contract) {
    
    contract.call = function (options) {
        contract._isTransact = false;
        contract._options = options;
        return contract;
Marek Kotewicz's avatar
Marek Kotewicz committed
43 44
    };

Marek Kotewicz's avatar
Marek Kotewicz committed
45 46 47 48
    contract.transact = function (options) {
        contract._isTransact = true;
        contract._options = options;
        return contract;
Marek Kotewicz's avatar
Marek Kotewicz committed
49 50
    };

Marek Kotewicz's avatar
Marek Kotewicz committed
51
    contract._options = {};
52
    ['gas', 'gasPrice', 'value', 'from'].forEach(function(p) {
Marek Kotewicz's avatar
Marek Kotewicz committed
53 54 55
        contract[p] = function (v) {
            contract._options[p] = v;
            return contract;
56 57 58
        };
    });

Marek Kotewicz's avatar
Marek Kotewicz committed
59 60 61 62 63
};

var addFunctionsToContract = function (contract, desc, address) {
    var inputParser = abi.inputParser(desc);
    var outputParser = abi.outputParser(desc);
64

Marek Kotewicz's avatar
Marek Kotewicz committed
65
    // create contract functions
66
    utils.filterFunctions(desc).forEach(function (method) {
67

68 69
        var displayName = utils.extractDisplayName(method.name);
        var typeName = utils.extractTypeName(method.name);
70 71

        var impl = function () {
72
            var params = Array.prototype.slice.call(arguments);
73
            var signature = abi.signatureFromAscii(method.name);
74 75
            var parsed = inputParser[displayName][typeName].apply(null, params);

Marek Kotewicz's avatar
Marek Kotewicz committed
76
            var options = contract._options || {};
77 78
            options.to = address;
            options.data = signature + parsed;
79
            
Marek Kotewicz's avatar
Marek Kotewicz committed
80
            var isTransact = contract._isTransact === true || (contract._isTransact !== false && !method.constant);
Gav Wood's avatar
Gav Wood committed
81
            var collapse = options.collapse !== false;
82 83
            
            // reset
Marek Kotewicz's avatar
Marek Kotewicz committed
84 85
            contract._options = {};
            contract._isTransact = null;
86

87
            if (isTransact) {
88 89 90 91 92 93 94
                
                exportNatspecGlobals({
                    abi: desc,
                    address: address,
                    method: method.name,
                    params: params
                });
95

96 97 98
                // transactions do not have any output, cause we do not know, when they will be processed
                web3.eth.transact(options);
                return;
99
            }
100 101
            
            var output = web3.eth.call(options);
Gav Wood's avatar
Gav Wood committed
102 103 104
            var ret = outputParser[displayName][typeName](output);
            if (collapse)
            {
105
                if (ret.length === 1)
Gav Wood's avatar
Gav Wood committed
106
                    ret = ret[0];
107
                else if (ret.length === 0)
Gav Wood's avatar
Gav Wood committed
108 109 110
                    ret = null;
            }
            return ret;
111
        };
112

Marek Kotewicz's avatar
Marek Kotewicz committed
113 114
        if (contract[displayName] === undefined) {
            contract[displayName] = impl;
115 116
        }

Marek Kotewicz's avatar
Marek Kotewicz committed
117
        contract[displayName][typeName] = impl;
118
    });
Marek Kotewicz's avatar
Marek Kotewicz committed
119
};
120

Marek Kotewicz's avatar
Marek Kotewicz committed
121 122
var addEventRelatedPropertiesToContract = function (contract, desc, address) {
    contract.address = address;
Marek Kotewicz's avatar
Marek Kotewicz committed
123 124 125 126 127
    contract._onWatchEventResult = function (data) {
        var matchingEvent = event.getMatchingEvent(utils.filterEvents(desc));
        var parser = eventImpl.outputParser(matchingEvent);
        return parser(data);
    };
Marek Kotewicz's avatar
Marek Kotewicz committed
128
    
Marek Kotewicz's avatar
Marek Kotewicz committed
129
    Object.defineProperty(contract, 'topic', {
Marek Kotewicz's avatar
Marek Kotewicz committed
130
        get: function() {
131
            return utils.filterEvents(desc).map(function (e) {
Marek Kotewicz's avatar
Marek Kotewicz committed
132
                return abi.eventSignatureFromAscii(e.name);
Marek Kotewicz's avatar
Marek Kotewicz committed
133 134 135
            });
        }
    });
Marek Kotewicz's avatar
Marek Kotewicz committed
136

Marek Kotewicz's avatar
Marek Kotewicz committed
137
};
Marek Kotewicz's avatar
Marek Kotewicz committed
138

Marek Kotewicz's avatar
Marek Kotewicz committed
139 140
var addEventsToContract = function (contract, desc, address) {
    // create contract events
141
    utils.filterEvents(desc).forEach(function (e) {
Marek Kotewicz's avatar
Marek Kotewicz committed
142

Marek Kotewicz's avatar
Marek Kotewicz committed
143 144
        var impl = function () {
            var params = Array.prototype.slice.call(arguments);
Marek Kotewicz's avatar
Marek Kotewicz committed
145
            var signature = abi.eventSignatureFromAscii(e.name);
146
            var event = eventImpl.inputParser(address, signature, e);
Marek Kotewicz's avatar
Marek Kotewicz committed
147
            var o = event.apply(null, params);
Marek Kotewicz's avatar
Marek Kotewicz committed
148 149 150 151
            o._onWatchEventResult = function (data) {
                var parser = eventImpl.outputParser(e);
                return parser(data);
            };
Marek Kotewicz's avatar
Marek Kotewicz committed
152 153
            return web3.eth.watch(o);  
        };
Marek Kotewicz's avatar
Marek Kotewicz committed
154 155 156
        
        // this property should be used by eth.filter to check if object is an event
        impl._isEvent = true;
Marek Kotewicz's avatar
Marek Kotewicz committed
157

158 159
        var displayName = utils.extractDisplayName(e.name);
        var typeName = utils.extractTypeName(e.name);
Marek Kotewicz's avatar
Marek Kotewicz committed
160

Marek Kotewicz's avatar
Marek Kotewicz committed
161 162
        if (contract[displayName] === undefined) {
            contract[displayName] = impl;
Marek Kotewicz's avatar
Marek Kotewicz committed
163 164
        }

Marek Kotewicz's avatar
Marek Kotewicz committed
165
        contract[displayName][typeName] = impl;
Marek Kotewicz's avatar
Marek Kotewicz committed
166 167

    });
Marek Kotewicz's avatar
Marek Kotewicz committed
168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194
};


/**
 * This method should be called when we want to call / transact some solidity method from javascript
 * it returns an object which has same methods available as solidity contract description
 * usage example: 
 *
 * var abi = [{
 *      name: 'myMethod',
 *      inputs: [{ name: 'a', type: 'string' }],
 *      outputs: [{name: 'd', type: 'string' }]
 * }];  // contract abi
 *
 * var myContract = web3.eth.contract('0x0123123121', abi); // creation of contract object
 *
 * myContract.myMethod('this is test string param for call'); // myMethod call (implicit, default)
 * myContract.call().myMethod('this is test string param for call'); // myMethod call (explicit)
 * myContract.transact().myMethod('this is test string param for transact'); // myMethod transact
 *
 * @param address - address of the contract, which should be called
 * @param desc - abi json description of the contract, which is being created
 * @returns contract object
 */

var contract = function (address, desc) {

Marek Kotewicz's avatar
Marek Kotewicz committed
195 196 197 198
    // workaround for invalid assumption that method.name is the full anonymous prototype of the method.
    // it's not. it's just the name. the rest of the code assumes it's actually the anonymous
    // prototype, so we make it so as a workaround.
    // TODO: we may not want to modify input params, maybe use copy instead?
Marek Kotewicz's avatar
Marek Kotewicz committed
199 200 201 202 203 204 205 206 207 208 209 210 211
    desc.forEach(function (method) {
        if (method.name.indexOf('(') === -1) {
            var displayName = method.name;
            var typeName = method.inputs.map(function(i){return i.type; }).join();
            method.name = displayName + '(' + typeName + ')';
        }
    });

    var result = {};
    addFunctionRelatedPropertiesToContract(result);
    addFunctionsToContract(result, desc, address);
    addEventRelatedPropertiesToContract(result, desc, address);
    addEventsToContract(result, desc, address);
Marek Kotewicz's avatar
Marek Kotewicz committed
212

Marek Kotewicz's avatar
Marek Kotewicz committed
213
    return result;
214 215 216
};

module.exports = contract;
Marek Kotewicz's avatar
Marek Kotewicz committed
217