mirror of
https://github.com/ethereum/solidity
synced 2023-10-03 13:03:40 +00:00
Split creating contracts
This commit is contained in:
parent
40e1283d72
commit
26d47c7213
@ -12,123 +12,7 @@ variables. Calling a function on a different contract (instance) will perform
|
||||
an EVM function call and thus switch the context such that state variables are
|
||||
inaccessible.
|
||||
|
||||
.. index:: ! contract;creation, constructor
|
||||
|
||||
******************
|
||||
Creating Contracts
|
||||
******************
|
||||
|
||||
Contracts can be created "from outside" via Ethereum transactions or from within Solidity contracts.
|
||||
|
||||
IDEs, such as `Remix <https://remix.ethereum.org/>`_, make the creation process seamless using UI elements.
|
||||
|
||||
Creating contracts programmatically on Ethereum is best done via using the JavaScript API `web3.js <https://github.com/ethereum/web3.js>`_.
|
||||
It has a function called `web3.eth.Contract <https://web3js.readthedocs.io/en/1.0/web3-eth-contract.html#new-contract>`_
|
||||
to facilitate contract creation.
|
||||
|
||||
When a contract is created, its constructor_ (a function declared with the ``constructor`` keyword) is executed once.
|
||||
|
||||
A constructor is optional. Only one constructor is allowed, which means
|
||||
overloading is not supported.
|
||||
|
||||
After the constructor has executed, the final code of the contract is deployed to the
|
||||
blockchain. This code includes all public and external functions and all functions
|
||||
that are reachable from there through function calls. The deployed code does not
|
||||
include the constructor code or internal functions only called from the constructor.
|
||||
|
||||
.. index:: constructor;arguments
|
||||
|
||||
Internally, constructor arguments are passed :ref:`ABI encoded <ABI>` after the code of
|
||||
the contract itself, but you do not have to care about this if you use ``web3.js``.
|
||||
|
||||
If a contract wants to create another contract, the source code
|
||||
(and the binary) of the created contract has to be known to the creator.
|
||||
This means that cyclic creation dependencies are impossible.
|
||||
|
||||
::
|
||||
|
||||
pragma solidity >=0.4.22 <0.6.0;
|
||||
|
||||
contract OwnedToken {
|
||||
// `TokenCreator` is a contract type that is defined below.
|
||||
// It is fine to reference it as long as it is not used
|
||||
// to create a new contract.
|
||||
TokenCreator creator;
|
||||
address owner;
|
||||
bytes32 name;
|
||||
|
||||
// This is the constructor which registers the
|
||||
// creator and the assigned name.
|
||||
constructor(bytes32 _name) public {
|
||||
// State variables are accessed via their name
|
||||
// and not via e.g. `this.owner`. Functions can
|
||||
// be accessed directly or through `this.f`,
|
||||
// but the latter provides an external view
|
||||
// to the function. Especially in the constructor,
|
||||
// you should not access functions externally,
|
||||
// because the function does not exist yet.
|
||||
// See the next section for details.
|
||||
owner = msg.sender;
|
||||
|
||||
// We do an explicit type conversion from `address`
|
||||
// to `TokenCreator` and assume that the type of
|
||||
// the calling contract is `TokenCreator`, there is
|
||||
// no real way to check that.
|
||||
creator = TokenCreator(msg.sender);
|
||||
name = _name;
|
||||
}
|
||||
|
||||
function changeName(bytes32 newName) public {
|
||||
// Only the creator can alter the name --
|
||||
// the comparison is possible since contracts
|
||||
// are explicitly convertible to addresses.
|
||||
if (msg.sender == address(creator))
|
||||
name = newName;
|
||||
}
|
||||
|
||||
function transfer(address newOwner) public {
|
||||
// Only the current owner can transfer the token.
|
||||
if (msg.sender != owner) return;
|
||||
|
||||
// We ask the creator contract if the transfer
|
||||
// should proceed by using a function of the
|
||||
// `TokenCreator` contract defined below. If
|
||||
// the call fails (e.g. due to out-of-gas),
|
||||
// the execution also fails here.
|
||||
if (creator.isTokenTransferOK(owner, newOwner))
|
||||
owner = newOwner;
|
||||
}
|
||||
}
|
||||
|
||||
contract TokenCreator {
|
||||
function createToken(bytes32 name)
|
||||
public
|
||||
returns (OwnedToken tokenAddress)
|
||||
{
|
||||
// Create a new `Token` contract and return its address.
|
||||
// From the JavaScript side, the return type is
|
||||
// `address`, as this is the closest type available in
|
||||
// the ABI.
|
||||
return new OwnedToken(name);
|
||||
}
|
||||
|
||||
function changeName(OwnedToken tokenAddress, bytes32 name) public {
|
||||
// Again, the external type of `tokenAddress` is
|
||||
// simply `address`.
|
||||
tokenAddress.changeName(name);
|
||||
}
|
||||
|
||||
// Perform checks to determine if transferring a token to the
|
||||
// `OwnedToken` contract should proceed
|
||||
function isTokenTransferOK(address currentOwner, address newOwner)
|
||||
public
|
||||
pure
|
||||
returns (bool ok)
|
||||
{
|
||||
// Check an arbitrary condition to see if transfer should proceed
|
||||
return keccak256(abi.encodePacked(currentOwner, newOwner))[0] == 0x7f;
|
||||
}
|
||||
}
|
||||
.. include:: contracts/creating-contracts.rst
|
||||
|
||||
.. index:: ! visibility, external, public, private, internal
|
||||
|
||||
|
117
docs/contracts/creating-contracts.rst
Normal file
117
docs/contracts/creating-contracts.rst
Normal file
@ -0,0 +1,117 @@
|
||||
.. index:: ! contract;creation, constructor
|
||||
|
||||
******************
|
||||
Creating Contracts
|
||||
******************
|
||||
|
||||
Contracts can be created "from outside" via Ethereum transactions or from within Solidity contracts.
|
||||
|
||||
IDEs, such as `Remix <https://remix.ethereum.org/>`_, make the creation process seamless using UI elements.
|
||||
|
||||
Creating contracts programmatically on Ethereum is best done via using the JavaScript API `web3.js <https://github.com/ethereum/web3.js>`_.
|
||||
It has a function called `web3.eth.Contract <https://web3js.readthedocs.io/en/1.0/web3-eth-contract.html#new-contract>`_
|
||||
to facilitate contract creation.
|
||||
|
||||
When a contract is created, its :ref:`constructor <constructor>` (a function declared with the ``constructor`` keyword) is executed once.
|
||||
|
||||
A constructor is optional. Only one constructor is allowed, which means
|
||||
overloading is not supported.
|
||||
|
||||
After the constructor has executed, the final code of the contract is deployed to the
|
||||
blockchain. This code includes all public and external functions and all functions
|
||||
that are reachable from there through function calls. The deployed code does not
|
||||
include the constructor code or internal functions only called from the constructor.
|
||||
|
||||
.. index:: constructor;arguments
|
||||
|
||||
Internally, constructor arguments are passed :ref:`ABI encoded <ABI>` after the code of
|
||||
the contract itself, but you do not have to care about this if you use ``web3.js``.
|
||||
|
||||
If a contract wants to create another contract, the source code
|
||||
(and the binary) of the created contract has to be known to the creator.
|
||||
This means that cyclic creation dependencies are impossible.
|
||||
|
||||
::
|
||||
|
||||
pragma solidity >=0.4.22 <0.6.0;
|
||||
|
||||
contract OwnedToken {
|
||||
// `TokenCreator` is a contract type that is defined below.
|
||||
// It is fine to reference it as long as it is not used
|
||||
// to create a new contract.
|
||||
TokenCreator creator;
|
||||
address owner;
|
||||
bytes32 name;
|
||||
|
||||
// This is the constructor which registers the
|
||||
// creator and the assigned name.
|
||||
constructor(bytes32 _name) public {
|
||||
// State variables are accessed via their name
|
||||
// and not via e.g. `this.owner`. Functions can
|
||||
// be accessed directly or through `this.f`,
|
||||
// but the latter provides an external view
|
||||
// to the function. Especially in the constructor,
|
||||
// you should not access functions externally,
|
||||
// because the function does not exist yet.
|
||||
// See the next section for details.
|
||||
owner = msg.sender;
|
||||
|
||||
// We do an explicit type conversion from `address`
|
||||
// to `TokenCreator` and assume that the type of
|
||||
// the calling contract is `TokenCreator`, there is
|
||||
// no real way to check that.
|
||||
creator = TokenCreator(msg.sender);
|
||||
name = _name;
|
||||
}
|
||||
|
||||
function changeName(bytes32 newName) public {
|
||||
// Only the creator can alter the name --
|
||||
// the comparison is possible since contracts
|
||||
// are explicitly convertible to addresses.
|
||||
if (msg.sender == address(creator))
|
||||
name = newName;
|
||||
}
|
||||
|
||||
function transfer(address newOwner) public {
|
||||
// Only the current owner can transfer the token.
|
||||
if (msg.sender != owner) return;
|
||||
|
||||
// We ask the creator contract if the transfer
|
||||
// should proceed by using a function of the
|
||||
// `TokenCreator` contract defined below. If
|
||||
// the call fails (e.g. due to out-of-gas),
|
||||
// the execution also fails here.
|
||||
if (creator.isTokenTransferOK(owner, newOwner))
|
||||
owner = newOwner;
|
||||
}
|
||||
}
|
||||
|
||||
contract TokenCreator {
|
||||
function createToken(bytes32 name)
|
||||
public
|
||||
returns (OwnedToken tokenAddress)
|
||||
{
|
||||
// Create a new `Token` contract and return its address.
|
||||
// From the JavaScript side, the return type is
|
||||
// `address`, as this is the closest type available in
|
||||
// the ABI.
|
||||
return new OwnedToken(name);
|
||||
}
|
||||
|
||||
function changeName(OwnedToken tokenAddress, bytes32 name) public {
|
||||
// Again, the external type of `tokenAddress` is
|
||||
// simply `address`.
|
||||
tokenAddress.changeName(name);
|
||||
}
|
||||
|
||||
// Perform checks to determine if transferring a token to the
|
||||
// `OwnedToken` contract should proceed
|
||||
function isTokenTransferOK(address currentOwner, address newOwner)
|
||||
public
|
||||
pure
|
||||
returns (bool ok)
|
||||
{
|
||||
// Check an arbitrary condition to see if transfer should proceed
|
||||
return keccak256(abi.encodePacked(currentOwner, newOwner))[0] == 0x7f;
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue
Block a user